Screw convention; moving include files alongside source files.

They now will show up in xcode/etc.
This commit is contained in:
Ben Vanik
2013-02-06 02:19:50 -08:00
parent 3dfd9c4b00
commit 88431eadce
151 changed files with 290 additions and 200 deletions

72
src/xenia/assert.h Normal file
View File

@@ -0,0 +1,72 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_ASSERT_H_
#define XENIA_ASSERT_H_
#include <assert.h>
#include <xenia/assert.h>
#include <xenia/config.h>
#include <xenia/platform.h>
#include <xenia/platform_includes.h>
#include <xenia/types.h>
#if XE_COMPILER(MSVC)
// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
#if !defined(__WFILE__)
#define WIDEN2(x) L##x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define __WFUNCTION__ WIDEN(__FUNCTION__)
#endif
#define XE_CURRENT_FILE __WFILE__
#define XE_CURRENT_FUNCTION __WFUNCTION__
#else
#define XE_CURRENT_FILE __FILE__
#define XE_CURRENT_FUNCTION __FUNCTION__
#endif // MSVC
#define XE_CURRENT_LINE __LINE__
#define __XE_ASSERT(expr) assert(expr)
#if XE_OPTION(ENABLE_ASSERTS)
#define XEASSERTCORE(expr) __XE_ASSERT(expr)
#else
#define XEASSERTCORE(expr) XE_EMPTY_MACRO
#endif // ENABLE_ASSERTS
#define XEASSERTALWAYS() XEASSERTCORE( 0 )
#define XEASSERT(expr) XEASSERTCORE( (expr) )
#define XEASSERTTRUE(expr) XEASSERTCORE( (expr) )
#define XEASSERTFALSE(expr) XEASSERTCORE(!(expr) )
#define XEASSERTZERO(expr) XEASSERTCORE( (expr) == 0 )
#define XEASSERTNOTZERO(expr) XEASSERTCORE( (expr) != 0 )
#define XEASSERTNULL(expr) XEASSERTCORE( (expr) == NULL )
#define XEASSERTNOTNULL(expr) XEASSERTCORE( (expr) != NULL )
#if XE_COMPILER(MSVC)
// http://msdn.microsoft.com/en-us/library/bb918086.aspx
// TODO(benvanik): if 2010+, use static_assert?
// http://msdn.microsoft.com/en-us/library/dd293588.aspx
#define XESTATICASSERT(expr, message) _STATIC_ASSERT(expr)
//#elif XE_COMPILER(GNUC)
// http://stackoverflow.com/questions/3385515/static-assert-in-c
//#define XESTATICASSERT(expr, message) ({ extern int __attribute__((error("assertion failure: '" #expr "' not true - " #message))) compile_time_check(); ((expr)?0:compile_time_check()),0; })
#else
// http://stackoverflow.com/questions/3385515/static-assert-in-c
#define XESTATICASSERT3(expr, L) typedef char static_assertion_##L[(expr)?1:-1]
#define XESTATICASSERT2(expr, L) XESTATICASSERT3(expr, L)
#define XESTATICASSERT(expr, message) XESTATICASSERT2(expr, __LINE__)
#endif // MSVC
#endif // XENIA_ASSERT_H_

81
src/xenia/atomic.h Normal file
View File

@@ -0,0 +1,81 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_ATOMIC_H_
#define XENIA_ATOMIC_H_
#include <xenia/platform.h>
#include <xenia/platform_includes.h>
// These functions are modeled off of the Apple OSAtomic routines
// http://developer.apple.com/library/mac/#documentation/DriversKernelHardware/Reference/libkern_ref/OSAtomic_h/
#if XE_LIKE(OSX)
#include <libkern/OSAtomic.h>
#define xe_atomic_inc_32(value) \
OSAtomicIncrement32Barrier(value)
#define xe_atomic_dec_32(value) \
OSAtomicDecrement32Barrier(value)
#define xe_atomic_add_32(amount, value) \
((void)OSAtomicAdd32Barrier(amount, value))
#define xe_atomic_sub_32(amount, value) \
((void)OSAtomicAdd32Barrier(-amount, value))
#define xe_atomic_cas_32(oldValue, newValue, value) \
OSAtomicCompareAndSwap32Barrier(oldValue, newValue, value)
typedef OSQueueHead xe_atomic_stack_t;
#define xe_atomic_stack_init(stack) \
*(stack) = (OSQueueHead)OS_ATOMIC_QUEUE_INIT
#define xe_atomic_stack_enqueue(stack, item, offset) \
OSAtomicEnqueue((OSQueueHead*)stack, item, offset)
#define xe_atomic_stack_dequeue(stack, offset) \
OSAtomicDequeue((OSQueueHead*)stack, offset)
#elif XE_LIKE(WIN32)
#define xe_atomic_inc_32(value) \
InterlockedIncrement((volatile LONG*)value)
#define xe_atomic_dec_32(value) \
InterlockedDecrement((volatile LONG*)value)
#define xe_atomic_add_32(amount, value) \
((void)InterlockedExchangeAdd((volatile LONG*)value, amount))
#define xe_atomic_sub_32(amount, value) \
((void)InterlockedExchangeSubtract((volatile unsigned*)value, amount))
#define xe_atomic_vas_32(oldValue, newValue, value) \
(InterlockedCompareExchange((volatile LONG*)value, newValue, oldValue) == oldValue)
typedef SLIST_HEADER xe_atomic_stack_t;
#define xe_atomic_stack_init(stack) \
InitializeSListHead((PSLIST_HEADER)stack)
#define xe_atomic_stack_enqueue(stack, item, offset) \
XEIGNORE(InterlockedPushEntrySList((PSLIST_HEADER)stack, (PSLIST_ENTRY)((byte*)item + offset)))
XEFORCEINLINE void* xe_atomic_stack_dequeue(xe_atomic_stack_t* stack,
const size_t offset) {
void* ptr = (void*)InterlockedPopEntrySList((PSLIST_HEADER)stack);
if (ptr) {
return (void*)(((uint8_t*)ptr) - offset);
} else {
return NULL;
}
}
#elif XE_LIKE(POSIX)
#error TODO(benvanik): POSIX atomic primitives
#else
#error No atomic primitives defined for this platform/cpu combination.
#endif // OSX
#endif // XENIA_ATOMIC_H_

77
src/xenia/byte_order.h Normal file
View File

@@ -0,0 +1,77 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BYTE_ORDER_H_
#define XENIA_BYTE_ORDER_H_
#include <xenia/platform.h>
#include <xenia/types.h>
#if XE_COMPILER(MSVC)
#define XESWAP16 _byteswap_ushort
#define XESWAP32 _byteswap_ulong
#define XESWAP64 _byteswap_uint64
#elif XE_LIKE(OSX)
#include <libkern/OSByteOrder.h>
#define XESWAP16 OSSwapInt16
#define XESWAP32 OSSwapInt32
#define XESWAP64 OSSwapInt64
#else
#define XESWAP16 bswap_16
#define XESWAP32 bswap_32
#define XESWAP64 bswap_64
#endif
#if XE_CPU(BIGENDIAN)
#define XESWAP16BE(p) (p)
#define XESWAP32BE(p) (p)
#define XESWAP64BE(p) (p)
#define XESWAP16LE(p) XESWAP16(p)
#define XESWAP32LE(p) XESWAP32(p)
#define XESWAP64LE(p) XESWAP64(p)
#else
#define XESWAP16BE(p) XESWAP16(p)
#define XESWAP32BE(p) XESWAP32(p)
#define XESWAP64BE(p) XESWAP64(p)
#define XESWAP16LE(p) (p)
#define XESWAP32LE(p) (p)
#define XESWAP64LE(p) (p)
#endif
#define XEGETINT8BE(p) ( (int8_t)(*(p)))
#define XEGETUINT8BE(p) ( (uint8_t)(*(p)))
#define XEGETINT16BE(p) ( (int16_t)XESWAP16BE(*(uint16_t*)(p)))
#define XEGETUINT16BE(p) ((uint16_t)XESWAP16BE(*(uint16_t*)(p)))
#define XEGETINT32BE(p) ( (int32_t)XESWAP32BE(*(uint32_t*)(p)))
#define XEGETUINT32BE(p) ((uint32_t)XESWAP32BE(*(uint32_t*)(p)))
#define XEGETINT64BE(p) ( (int64_t)XESWAP64BE(*(uint64_t*)(p)))
#define XEGETUINT64BE(p) ((uint64_t)XESWAP64BE(*(uint64_t*)(p)))
#define XEGETINT8LE(p) ( (int8_t)(*(p)))
#define XEGETUINT8LE(p) ( (uint8_t)(*(p)))
#define XEGETINT16LE(p) ( (int16_t)XESWAP16LE(*(uint16_t*)(p)))
#define XEGETUINT16LE(p) ((uint16_t)XESWAP16LE(*(uint16_t*)(p)))
#define XEGETINT32LE(p) ( (int32_t)XESWAP32LE(*(uint32_t*)(p)))
#define XEGETUINT32LE(p) ((uint32_t)XESWAP32LE(*(uint32_t*)(p)))
#define XEGETINT64LE(p) ( (int64_t)XESWAP64LE(*(uint64_t*)(p)))
#define XEGETUINT64LE(p) ((uint64_t)XESWAP64LE(*(uint64_t*)(p)))
#define XESETINT8BE(p, v) (*( (int8_t*)(p)) = (int8_t)v)
#define XESETUINT8BE(p, v) (*( (uint8_t*)(p)) = (uint8_t)v)
#define XESETINT16BE(p, v) (*( (int16_t*)(p)) = XESWAP16BE( (int16_t)v))
#define XESETUINT16BE(p, v) (*((uint16_t*)(p)) = XESWAP16BE((uint16_t)v))
#define XESETINT32BE(p, v) (*( (int32_t*)(p)) = XESWAP32BE( (int32_t)v))
#define XESETUINT32BE(p, v) (*((uint32_t*)(p)) = XESWAP32BE((uint32_t)v))
#define XESETINT64BE(p, v) (*( (int64_t*)(p)) = XESWAP64BE( (int64_t)v))
#define XESETUINT64BE(p, v) (*((uint64_t*)(p)) = XESWAP64BE((uint64_t)v))
#endif // XENIA_BYTE_ORDER_H_

24
src/xenia/common.h Normal file
View File

@@ -0,0 +1,24 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_COMMON_H_
#define XENIA_COMMON_H_
#include <xenia/assert.h>
#include <xenia/atomic.h>
#include <xenia/byte_order.h>
#include <xenia/config.h>
#include <xenia/logging.h>
#include <xenia/malloc.h>
#include <xenia/platform.h>
#include <xenia/platform_includes.h>
#include <xenia/string.h>
#include <xenia/types.h>
#endif // XENIA_COMMON_H_

37
src/xenia/config.h Normal file
View File

@@ -0,0 +1,37 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CONFIG_H_
#define XENIA_CONFIG_H_
#define XE_OPTION(NAME) (defined XE_OPTION_##NAME && XE_OPTION_##NAME)
// Enable compile-time and runtime-time assertions.
#define XE_OPTION_ENABLE_ASSERTS 1
// Enable general logging.
#define XE_OPTION_ENABLE_LOGGING 1
#define XE_OPTION_LOG_ERROR 1
#define XE_OPTION_LOG_WARNING 1
#define XE_OPTION_LOG_INFO 1
#define XE_OPTION_LOG_DEBUG 1
#define XE_OPTION_LOG_CPU 1
#define XE_OPTION_LOG_SDB 0
#define XE_OPTION_LOG_GPU 1
#define XE_OPTION_LOG_KERNEL 1
#define XE_OPTION_LOG_FS 1
// TODO(benvanik): make this a runtime option
#define XE_OPTION_OPTIMIZED 0
#endif // XENIA_CONFIG_H_

22
src/xenia/core.h Normal file
View File

@@ -0,0 +1,22 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CORE_H_
#define XENIA_CORE_H_
#include <xenia/common.h>
#include <xenia/core/file.h>
#include <xenia/core/memory.h>
#include <xenia/core/mmap.h>
#include <xenia/core/mutex.h>
#include <xenia/core/pal.h>
#include <xenia/core/ref.h>
#endif // XENIA_CORE_H_

85
src/xenia/core/file.cc Normal file
View File

@@ -0,0 +1,85 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/file.h>
typedef struct xe_file {
xe_ref_t ref;
void* handle;
} xe_file_t;
#if XE_LIKE(WIN32)
#define fseeko _fseeki64
#define ftello _ftelli64
#endif // WIN32
xe_file_ref xe_file_open(xe_pal_ref pal, const xe_file_mode mode,
const xechar_t *path) {
xe_file_ref file = (xe_file_ref)xe_calloc(sizeof(xe_file_t));
xe_ref_init((xe_ref)file);
xechar_t mode_string[10];
mode_string[0] = 0;
if (mode & kXEFileModeRead) {
XEIGNORE(xestrcat(mode_string, XECOUNT(mode_string), XT("r")));
}
if (mode & kXEFileModeWrite) {
XEIGNORE(xestrcat(mode_string, XECOUNT(mode_string), XT("w")));
}
XEIGNORE(xestrcat(mode_string, XECOUNT(mode_string), XT("b")));
#if XE_LIKE(WIN32)
XEEXPECTZERO(_wfopen_s((FILE**)&file->handle, path, mode_string));
#else
file->handle = fopen(path, mode_string);
XEEXPECTNOTNULL(file->handle);
#endif // WIN32
return file;
XECLEANUP:
xe_file_release(file);
return NULL;
}
void xe_file_dealloc(xe_file_ref file) {
FILE* handle = (FILE*)file->handle;
fclose(handle);
file->handle = NULL;
}
xe_file_ref xe_file_retain(xe_file_ref file) {
xe_ref_retain((xe_ref)file);
return file;
}
void xe_file_release(xe_file_ref file) {
xe_ref_release((xe_ref)file, (xe_ref_dealloc_t)xe_file_dealloc);
}
size_t xe_file_get_length(xe_file_ref file) {
FILE* handle = (FILE*)file->handle;
fseeko(handle, 0, SEEK_END);
return ftello(handle);
}
size_t xe_file_read(xe_file_ref file, const size_t offset,
uint8_t *buffer, const size_t buffer_size) {
FILE* handle = (FILE*)file->handle;
if (fseeko(handle, offset, SEEK_SET) != 0) {
return -1;
}
return fread(buffer, 1, buffer_size, handle);
}

37
src/xenia/core/file.h Normal file
View File

@@ -0,0 +1,37 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CORE_FILE_H_
#define XENIA_CORE_FILE_H_
#include <xenia/common.h>
#include <xenia/core/pal.h>
#include <xenia/core/ref.h>
struct xe_file;
typedef struct xe_file* xe_file_ref;
typedef enum {
kXEFileModeRead = (1 << 0),
kXEFileModeWrite = (1 << 1),
} xe_file_mode;
xe_file_ref xe_file_open(xe_pal_ref pal, const xe_file_mode mode,
const xechar_t *path);
xe_file_ref xe_file_retain(xe_file_ref file);
void xe_file_release(xe_file_ref file);
size_t xe_file_get_length(xe_file_ref file);
size_t xe_file_read(xe_file_ref file, const size_t offset,
uint8_t *buffer, const size_t buffer_size);
#endif // XENIA_CORE_FILE_H_

189
src/xenia/core/memory.cc Normal file
View File

@@ -0,0 +1,189 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/memory.h>
#include <xenia/core/mutex.h>
#if !XE_PLATFORM(WIN32)
#include <sys/mman.h>
#endif // WIN32
#define MSPACES 1
#define USE_LOCKS 0
#define USE_DL_PREFIX 1
#define HAVE_MORECORE 0
#define HAVE_MMAP 0
#define HAVE_MREMAP 0
#define malloc_getpagesize 4096
#define DEFAULT_GRANULARITY 64 * 1024
#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T
#include <third_party/dlmalloc/malloc.c.h>
/**
* Memory map:
* 0x00000000 - 0x40000000 (1024mb) - virtual 4k pages
* 0x40000000 - 0x80000000 (1024mb) - virtual 64k pages
* 0x80000000 - 0x8C000000 ( 192mb) - xex 64k pages
* 0x8C000000 - 0x90000000 ( 64mb) - xex 64k pages (encrypted)
* 0x90000000 - 0xA0000000 ( 256mb) - xex 4k pages
* 0xA0000000 - 0xC0000000 ( 512mb) - physical 64k pages
*
* We use the host OS to create an entire addressable range for this. That way
* we don't have to emulate a TLB. It'd be really cool to pass through page
* sizes or use madvice to let the OS know what to expect.
*/
struct xe_memory {
xe_ref_t ref;
size_t length;
void* ptr;
xe_mutex_t* heap_mutex;
mspace heap;
};
xe_memory_ref xe_memory_create(xe_pal_ref pal, xe_memory_options_t options) {
uint32_t offset;
uint32_t mspace_size;
xe_memory_ref memory = (xe_memory_ref)xe_calloc(sizeof(xe_memory));
xe_ref_init((xe_ref)memory);
memory->length = 0xC0000000;
#if XE_PLATFORM(WIN32)
memory->ptr = VirtualAlloc(0, memory->length,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
#else
memory->ptr = mmap(0, memory->length, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
XEEXPECT(memory->ptr != MAP_FAILED);
#endif // WIN32
XEEXPECTNOTNULL(memory->ptr);
memory->heap_mutex = xe_mutex_alloc(0);
XEEXPECTNOTNULL(memory->heap_mutex);
// Allocate the mspace for our heap.
// We skip the first page to make writes to 0 easier to find.
offset = 64 * 1024;
mspace_size = 512 * 1024 * 1024 - offset;
memory->heap = create_mspace_with_base(
(uint8_t*)memory->ptr + offset, mspace_size, 0);
return memory;
XECLEANUP:
xe_memory_release(memory);
return NULL;
}
void xe_memory_dealloc(xe_memory_ref memory) {
if (memory->heap_mutex && memory->heap) {
xe_mutex_lock(memory->heap_mutex);
destroy_mspace(memory->heap);
memory->heap = NULL;
xe_mutex_unlock(memory->heap_mutex);
}
if (memory->heap_mutex) {
xe_mutex_free(memory->heap_mutex);
memory->heap_mutex = NULL;
}
#if XE_PLATFORM(WIN32)
XEIGNORE(VirtualFree(memory->ptr, memory->length, MEM_RELEASE));
#else
munmap(memory->ptr, memory->length);
#endif // WIN32
}
xe_memory_ref xe_memory_retain(xe_memory_ref memory) {
xe_ref_retain((xe_ref)memory);
return memory;
}
void xe_memory_release(xe_memory_ref memory) {
xe_ref_release((xe_ref)memory, (xe_ref_dealloc_t)xe_memory_dealloc);
}
size_t xe_memory_get_length(xe_memory_ref memory) {
return memory->length;
}
uint8_t *xe_memory_addr(xe_memory_ref memory, uint32_t guest_addr) {
return (uint8_t*)memory->ptr + guest_addr;
}
uint32_t xe_memory_search_aligned(xe_memory_ref memory, uint32_t start,
uint32_t end, const uint32_t *values,
const size_t value_count) {
XEASSERT(start <= end);
const uint32_t *p = (const uint32_t*)xe_memory_addr(memory, start);
const uint32_t *pe = (const uint32_t*)xe_memory_addr(memory, end);
while (p != pe) {
if (*p == values[0]) {
const uint32_t *pc = p + 1;
size_t matched = 1;
for (size_t n = 1; n < value_count; n++, pc++) {
if (*pc != values[n]) {
break;
}
matched++;
}
if (matched == value_count) {
return (uint32_t)((uint8_t*)p - (uint8_t*)memory->ptr);
}
}
p++;
}
return 0;
}
// TODO(benvanik): reserve/commit states/large pages/etc
uint32_t xe_memory_heap_alloc(xe_memory_ref memory, uint32_t base_addr,
uint32_t size, uint32_t flags) {
if (base_addr) {
return base_addr;
}
XEASSERT(base_addr == 0);
XEASSERT(flags == 0);
XEIGNORE(xe_mutex_lock(memory->heap_mutex));
uint8_t* p = (uint8_t*)mspace_malloc(memory->heap, size);
XEIGNORE(xe_mutex_unlock(memory->heap_mutex));
if (!p) {
return 0;
}
return (uint32_t)((uintptr_t)p - (uintptr_t)memory->ptr);
}
uint32_t xe_memory_heap_free(xe_memory_ref memory, uint32_t addr,
uint32_t flags) {
XEASSERT(flags == 0);
uint8_t* p = (uint8_t*)memory->ptr + addr;
size_t real_size = mspace_usable_size(p);
if (!real_size) {
return 0;
}
XEIGNORE(xe_mutex_lock(memory->heap_mutex));
mspace_free(memory->heap, p);
XEIGNORE(xe_mutex_unlock(memory->heap_mutex));
return (uint32_t)real_size;
}

46
src/xenia/core/memory.h Normal file
View File

@@ -0,0 +1,46 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CORE_MEMORY_H_
#define XENIA_CORE_MEMORY_H_
#include <xenia/common.h>
#include <xenia/core/pal.h>
#include <xenia/core/ref.h>
typedef struct {
int reserved;
} xe_memory_options_t;
struct xe_memory;
typedef struct xe_memory* xe_memory_ref;
xe_memory_ref xe_memory_create(xe_pal_ref pal, xe_memory_options_t options);
xe_memory_ref xe_memory_retain(xe_memory_ref memory);
void xe_memory_release(xe_memory_ref memory);
size_t xe_memory_get_length(xe_memory_ref memory);
uint8_t *xe_memory_addr(xe_memory_ref memory, uint32_t guest_addr);
uint32_t xe_memory_search_aligned(xe_memory_ref memory, uint32_t start,
uint32_t end, const uint32_t *values,
const size_t value_count);
// These methods slice off memory from the virtual address space.
// They should only be used by kernel modules that know what they are doing.
uint32_t xe_memory_heap_alloc(xe_memory_ref memory, uint32_t base_addr,
uint32_t size, uint32_t flags);
uint32_t xe_memory_heap_free(xe_memory_ref memory, uint32_t addr,
uint32_t flags);
#endif // XENIA_CORE_MEMORY_H_

32
src/xenia/core/mmap.h Normal file
View File

@@ -0,0 +1,32 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CORE_MMAP_H_
#define XENIA_CORE_MMAP_H_
#include <xenia/common.h>
#include <xenia/core/file.h>
#include <xenia/core/pal.h>
#include <xenia/core/ref.h>
struct xe_mmap;
typedef struct xe_mmap* xe_mmap_ref;
xe_mmap_ref xe_mmap_open(xe_pal_ref pal, const xe_file_mode mode,
const xechar_t *path,
const size_t offset, const size_t length);
xe_mmap_ref xe_mmap_retain(xe_mmap_ref mmap);
void xe_mmap_release(xe_mmap_ref mmap);
uint8_t* xe_mmap_get_addr(xe_mmap_ref mmap);
size_t xe_mmap_get_length(xe_mmap_ref mmap);
#endif // XENIA_CORE_MMAP_H_

View File

@@ -0,0 +1,101 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/mmap.h>
#include <sys/mman.h>
typedef struct xe_mmap {
xe_ref_t ref;
void* file_handle;
void* mmap_handle;
void* addr;
size_t length;
} xe_mmap_t;
xe_mmap_ref xe_mmap_open(xe_pal_ref pal, const xe_file_mode mode,
const xechar_t *path,
const size_t offset, const size_t length) {
xe_mmap_ref mmap = (xe_mmap_ref)xe_calloc(sizeof(xe_mmap_t));
xe_ref_init((xe_ref)mmap);
xechar_t mode_string[10];
mode_string[0] = 0;
if (mode & kXEFileModeRead) {
XEIGNORE(xestrcat(mode_string, XECOUNT(mode_string), XT("r")));
}
if (mode & kXEFileModeWrite) {
XEIGNORE(xestrcat(mode_string, XECOUNT(mode_string), XT("w")));
}
XEIGNORE(xestrcat(mode_string, XECOUNT(mode_string), XT("b")));
int prot = 0;
if (mode & kXEFileModeRead) {
prot |= PROT_READ;
}
if (mode & kXEFileModeWrite) {
prot |= PROT_WRITE;
}
FILE* file_handle = fopen(path, mode_string);
mmap->file_handle = file_handle;
XEEXPECTNOTNULL(mmap->file_handle);
size_t map_length;
map_length = length;
if (!length) {
fseeko(file_handle, 0, SEEK_END);
map_length = ftello(file_handle);
}
mmap->length = map_length;
mmap->addr = ::mmap(0, map_length, prot, MAP_SHARED, fileno(file_handle),
offset);
XEEXPECTNOTNULL(mmap->addr);
return mmap;
XECLEANUP:
xe_mmap_release(mmap);
return NULL;
}
void xe_mmap_dealloc(xe_mmap_ref mmap) {
if (mmap->addr) {
munmap(mmap->addr, mmap->length);
mmap->addr = NULL;
}
FILE* file_handle = (FILE*)mmap->file_handle;
if (file_handle) {
fclose(file_handle);
mmap->file_handle = NULL;
}
}
xe_mmap_ref xe_mmap_retain(xe_mmap_ref mmap) {
xe_ref_retain((xe_ref)mmap);
return mmap;
}
void xe_mmap_release(xe_mmap_ref mmap) {
xe_ref_release((xe_ref)mmap, (xe_ref_dealloc_t)xe_mmap_dealloc);
}
uint8_t* xe_mmap_get_addr(xe_mmap_ref mmap) {
return (uint8_t*)mmap->addr;
}
size_t xe_mmap_get_length(xe_mmap_ref mmap) {
return mmap->length;
}

132
src/xenia/core/mmap_win.cc Normal file
View File

@@ -0,0 +1,132 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/mmap.h>
typedef struct xe_mmap {
xe_ref_t ref;
HANDLE file_handle;
HANDLE mmap_handle;
void* addr;
size_t length;
} xe_mmap_t;
xe_mmap_ref xe_mmap_open(xe_pal_ref pal, const xe_file_mode mode,
const xechar_t *path,
const size_t offset, const size_t length) {
xe_mmap_ref mmap = (xe_mmap_ref)xe_calloc(sizeof(xe_mmap_t));
xe_ref_init((xe_ref)mmap);
DWORD fileAccess = 0;
DWORD fileShare = 0;
DWORD createMode = 0;
DWORD mappingProtect = 0;
DWORD viewAccess = 0;
switch (mode) {
case kXEFileModeRead:
fileAccess |= GENERIC_READ;
fileShare |= FILE_SHARE_READ;
createMode |= OPEN_EXISTING;
mappingProtect |= PAGE_READONLY;
viewAccess |= FILE_MAP_READ;
break;
case kXEFileModeWrite:
fileAccess |= GENERIC_WRITE;
fileShare |= 0;
createMode |= OPEN_ALWAYS;
mappingProtect |= PAGE_READWRITE;
viewAccess |= FILE_MAP_WRITE;
break;
case kXEFileModeRead | kXEFileModeWrite:
fileAccess |= GENERIC_READ | GENERIC_WRITE;
fileShare |= 0;
createMode |= OPEN_EXISTING;
mappingProtect |= PAGE_READWRITE;
viewAccess |= FILE_MAP_READ | FILE_MAP_WRITE;
break;
}
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
const size_t aligned_offset =
offset & (~(systemInfo.dwAllocationGranularity - 1));
const size_t aligned_length =
length + (offset - aligned_offset);
HANDLE file_handle = CreateFile(
path, fileAccess, fileShare, NULL, createMode, FILE_ATTRIBUTE_NORMAL,
NULL);
XEEXPECTNOTNULL(file_handle);
HANDLE handle = CreateFileMapping(
file_handle, NULL, mappingProtect, 0, 0, NULL);
//(DWORD)(aligned_length >> 32), (DWORD)(aligned_length & 0xFFFFFFFF), NULL);
XEEXPECTNOTNULL(handle);
void* address = MapViewOfFile(handle, viewAccess,
(DWORD)(aligned_offset >> 32),
(DWORD)(aligned_offset & 0xFFFFFFFF),
aligned_length);
XEEXPECTNOTNULL(address);
mmap->file_handle = file_handle;
mmap->mmap_handle = handle;
mmap->addr = address;
if (!length) {
mmap->length = length;
} else {
size_t map_length = GetFileSize(file_handle, NULL);
mmap->length = map_length;
}
return mmap;
XECLEANUP:
xe_mmap_release(mmap);
return NULL;
}
void xe_mmap_dealloc(xe_mmap_ref mmap) {
if (mmap->addr) {
UnmapViewOfFile(mmap->addr);
mmap->addr = NULL;
}
if (mmap->mmap_handle) {
CloseHandle(mmap->mmap_handle);
mmap->mmap_handle = NULL;
}
if (mmap->file_handle) {
CloseHandle(mmap->file_handle);
mmap->file_handle = NULL;
}
}
xe_mmap_ref xe_mmap_retain(xe_mmap_ref mmap) {
xe_ref_retain((xe_ref)mmap);
return mmap;
}
void xe_mmap_release(xe_mmap_ref mmap) {
xe_ref_release((xe_ref)mmap, (xe_ref_dealloc_t)xe_mmap_dealloc);
}
uint8_t* xe_mmap_get_addr(xe_mmap_ref mmap) {
return mmap->addr;
}
size_t xe_mmap_get_length(xe_mmap_ref mmap) {
return mmap->length;
}

27
src/xenia/core/mutex.h Normal file
View File

@@ -0,0 +1,27 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CORE_MUTEX_H_
#define XENIA_CORE_MUTEX_H_
#include <xenia/common.h>
typedef struct xe_mutex xe_mutex_t;
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count);
void xe_mutex_free(xe_mutex_t* mutex);
int xe_mutex_lock(xe_mutex_t* mutex);
int xe_mutex_trylock(xe_mutex_t* mutex);
int xe_mutex_unlock(xe_mutex_t* mutex);
#endif // XENIA_CORE_MUTEX_H_

View File

@@ -0,0 +1,60 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/mutex.h>
struct xe_mutex {
pthread_mutex_t value;
};
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count) {
xe_mutex_t* mutex = (xe_mutex_t*)xe_calloc(sizeof(xe_mutex_t));
int result = pthread_mutex_init(&mutex->value, NULL);
switch (result) {
case ENOMEM:
case EINVAL:
xe_free(mutex);
return NULL;
}
return mutex;
}
void xe_mutex_free(xe_mutex_t* mutex) {
int result = pthread_mutex_destroy(&mutex->value);
switch (result) {
case EBUSY:
case EINVAL:
break;
default:
break;
}
xe_free(mutex);
}
int xe_mutex_lock(xe_mutex_t* mutex) {
return pthread_mutex_lock(&mutex->value) == EINVAL ? 1 : 0;
}
int xe_mutex_trylock(xe_mutex_t* mutex) {
int result = pthread_mutex_trylock(&mutex->value);
switch (result) {
case EBUSY:
case EINVAL:
return 1;
default:
return 0;
}
}
int xe_mutex_unlock(xe_mutex_t* mutex) {
return pthread_mutex_unlock(&mutex->value) == EINVAL ? 1 : 0;
}

View File

@@ -0,0 +1,50 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/mutex.h>
struct xe_mutex {
CRITICAL_SECTION value;
};
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count) {
xe_mutex_t* mutex = (xe_mutex_t*)xe_calloc(sizeof(xe_mutex_t));
if (spin_count) {
InitializeCriticalSectionAndSpinCount(&mutex->value, spin_count);
} else {
InitializeCriticalSection(&mutex->value);
}
return mutex;
}
void xe_mutex_free(xe_mutex_t* mutex) {
DeleteCriticalSection(&mutex->value);
xe_free(mutex);
}
int xe_mutex_lock(xe_mutex_t* mutex) {
EnterCriticalSection(&mutex->value);
return 0;
}
int xe_mutex_trylock(xe_mutex_t* mutex) {
if (TryEnterCriticalSection(&mutex->value) == TRUE) {
return 0;
} else {
return 1;
}
}
int xe_mutex_unlock(xe_mutex_t* mutex) {
LeaveCriticalSection(&mutex->value);
return 0;
}

39
src/xenia/core/pal.cc Normal file
View File

@@ -0,0 +1,39 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/pal.h>
typedef struct xe_pal {
xe_ref_t ref;
} xe_pal_t;
xe_pal_ref xe_pal_create(xe_pal_options_t options) {
xe_pal_ref pal = (xe_pal_ref)xe_calloc(sizeof(xe_pal_t));
xe_ref_init((xe_ref)pal);
//
return pal;
}
void xe_pal_dealloc(xe_pal_ref pal) {
//
}
xe_pal_ref xe_pal_retain(xe_pal_ref pal) {
xe_ref_retain((xe_ref)pal);
return pal;
}
void xe_pal_release(xe_pal_ref pal) {
xe_ref_release((xe_ref)pal, (xe_ref_dealloc_t)xe_pal_dealloc);
}

31
src/xenia/core/pal.h Normal file
View File

@@ -0,0 +1,31 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CORE_PAL_H_
#define XENIA_CORE_PAL_H_
#include <xenia/common.h>
#include <xenia/core/ref.h>
typedef struct {
int reserved;
} xe_pal_options_t;
struct xe_pal;
typedef struct xe_pal* xe_pal_ref;
xe_pal_ref xe_pal_create(xe_pal_options_t options);
xe_pal_ref xe_pal_retain(xe_pal_ref pal);
void xe_pal_release(xe_pal_ref pal);
#endif // XENIA_CORE_PAL_H_

31
src/xenia/core/ref.cc Normal file
View File

@@ -0,0 +1,31 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/core/ref.h>
void xe_ref_init(xe_ref_t* ref) {
ref->count = 1;
}
void xe_ref_retain(xe_ref_t* ref) {
xe_atomic_inc_32(&ref->count);
}
void xe_ref_release(xe_ref_t* ref, xe_ref_dealloc_t dealloc) {
if (!ref) {
return;
}
if (!xe_atomic_dec_32(&ref->count)) {
if (dealloc) {
dealloc(ref);
}
xe_free(ref);
}
}

29
src/xenia/core/ref.h Normal file
View File

@@ -0,0 +1,29 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CORE_REF_H_
#define XENIA_CORE_REF_H_
#include <xenia/common.h>
typedef struct {
volatile int32_t count;
} xe_ref_t;
typedef xe_ref_t* xe_ref;
typedef void (*xe_ref_dealloc_t)(xe_ref);
void xe_ref_init(xe_ref ref);
void xe_ref_retain(xe_ref ref);
void xe_ref_release(xe_ref ref, xe_ref_dealloc_t dealloc);
#endif // XENIA_CORE_REF_H_

View File

@@ -0,0 +1,30 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'file.cc',
'file.h',
'memory.cc',
'memory.h',
'mmap.h',
'mutex.h',
'pal.cc',
'pal.h',
'ref.cc',
'ref.h',
],
'conditions': [
['OS == "mac" or OS == "linux"', {
'sources': [
'mmap_posix.cc',
'mutex_posix.cc',
],
}],
['OS == "win"', {
'sources': [
'mmap_win.cc',
'mutex_win.cc',
],
}],
],
}

View File

@@ -0,0 +1,45 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_CODEGEN_EMIT_H_
#define XENIA_CPU_CODEGEN_EMIT_H_
#include <xenia/cpu/ppc/instr.h>
namespace xe {
namespace cpu {
namespace codegen {
void RegisterEmitCategoryALU();
void RegisterEmitCategoryControl();
void RegisterEmitCategoryFPU();
void RegisterEmitCategoryMemory();
#define XEDISASMR(name, opcode, format) int InstrDisasm_##name
#define XEEMITTER(name, opcode, format) int InstrEmit_##name
#define XEREGISTERINSTR(name, opcode) \
RegisterInstrDisassemble(opcode, (InstrDisassembleFn)InstrDisasm_##name); \
RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name);
#define XEREGISTEREMITTER(name, opcode) \
RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name);
#define XEINSTRNOTIMPLEMENTED()
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
} // namespace codegen
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_CODEGEN_EMIT_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,824 @@
/*
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/codegen/emit.h>
#include <xenia/cpu/codegen/function_generator.h>
#include <xenia/cpu/ppc/state.h>
using namespace llvm;
using namespace xe::cpu::codegen;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
namespace xe {
namespace cpu {
namespace codegen {
int XeEmitIndirectBranchTo(
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
bool lk, uint32_t reg) {
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
// a normal function return that is pulling the LR from the stack that
// it set in the prolog. If so, we can omit the dynamic check!
// NOTE: we avoid spilling registers until we know that the target is not
// a basic block within this function.
Value* target;
switch (reg) {
case kXEPPCRegLR:
target = g.lr_value();
break;
case kXEPPCRegCTR:
target = g.ctr_value();
break;
default:
XEASSERTALWAYS();
return 1;
}
// Dynamic test when branching to LR, which is usually used for the return.
// We only do this if LK=0 as returns wouldn't set LR.
// Ideally it's a return and we can just do a simple ret and be done.
// If it's not, we fall through to the full indirection logic.
if (!lk && reg == kXEPPCRegLR) {
BasicBlock* next_block = g.GetNextBasicBlock();
BasicBlock* mismatch_bb = BasicBlock::Create(*g.context(), "lr_mismatch",
g.gen_fn(), next_block);
Value* lr_cmp = b.CreateICmpEQ(target, ++(g.gen_fn()->arg_begin()));
// The return block will spill registers for us.
b.CreateCondBr(lr_cmp, g.GetReturnBasicBlock(), mismatch_bb);
b.SetInsertPoint(mismatch_bb);
}
// Defer to the generator, which will do fancy things.
bool likely_local = !lk && reg == kXEPPCRegCTR;
return g.GenerateIndirectionBranch(cia, target, lk, likely_local);
}
int XeEmitBranchTo(
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
bool lk) {
// Get the basic block and switch behavior based on outgoing type.
FunctionBlock* fn_block = g.fn_block();
switch (fn_block->outgoing_type) {
case FunctionBlock::kTargetBlock:
{
BasicBlock* target_bb = g.GetBasicBlock(fn_block->outgoing_address);
XEASSERTNOTNULL(target_bb);
b.CreateBr(target_bb);
break;
}
case FunctionBlock::kTargetFunction:
{
// Spill all registers to memory.
// TODO(benvanik): only spill ones used by the target function? Use
// calling convention flags on the function to not spill temp
// registers?
g.SpillRegisters();
XEASSERTNOTNULL(fn_block->outgoing_function);
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
Function::arg_iterator args = g.gen_fn()->arg_begin();
Value* state_ptr = args;
BasicBlock* next_bb = g.GetNextBasicBlock();
if (!lk || !next_bb) {
// Tail. No need to refill the local register values, just return.
// We optimize this by passing in the LR from our parent instead of the
// next instruction. This allows the return from our callee to pop
// all the way up.
b.CreateCall2(target_fn, state_ptr, ++args);
b.CreateRetVoid();
} else {
// Will return here eventually.
// Refill registers from state.
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
g.FillRegisters();
b.CreateBr(next_bb);
}
break;
}
case FunctionBlock::kTargetLR:
{
// An indirect jump.
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegLR);
}
case FunctionBlock::kTargetCTR:
{
// An indirect jump.
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegCTR);
}
default:
case FunctionBlock::kTargetNone:
XEASSERTALWAYS();
return 1;
}
return 0;
}
XEDISASMR(bx, 0x48000000, I )(InstrData& i, InstrDisasm& d) {
d.Init("b", "Branch", i.I.LK ? InstrDisasm::kLR : 0);
uint32_t nia;
if (i.I.AA) {
nia = XEEXTS26(i.I.LI << 2);
} else {
nia = i.address + XEEXTS26(i.I.LI << 2);
}
d.AddUImmOperand(nia, 4);
return d.Finish();
}
XEEMITTER(bx, 0x48000000, I )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// if AA then
// NIA <- EXTS(LI || 0b00)
// else
// NIA <- CIA + EXTS(LI || 0b00)
// if LK then
// LR <- CIA + 4
uint32_t nia;
if (i.I.AA) {
nia = XEEXTS26(i.I.LI << 2);
} else {
nia = i.address + XEEXTS26(i.I.LI << 2);
}
if (i.I.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
}
return XeEmitBranchTo(g, b, "bx", i.address, i.I.LK);
}
XEDISASMR(bcx, 0x40000000, B )(InstrData& i, InstrDisasm& d) {
// TODO(benvanik): mnemonics
d.Init("bc", "Branch Conditional", i.B.LK ? InstrDisasm::kLR : 0);
if (!XESELECTBITS(i.B.BO, 2, 2)) {
d.AddCTR(InstrRegister::kReadWrite);
}
if (!XESELECTBITS(i.B.BO, 4, 4)) {
d.AddCR(i.B.BI >> 2, InstrRegister::kRead);
}
d.AddUImmOperand(i.B.BO, 1);
d.AddUImmOperand(i.B.BI, 1);
return d.Finish();
}
XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// if ¬BO[2] then
// CTR <- CTR - 1
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3])
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
// if ctr_ok & cond_ok then
// if AA then
// NIA <- EXTS(BD || 0b00)
// else
// NIA <- CIA + EXTS(BD || 0b00)
// if LK then
// LR <- CIA + 4
// NOTE: the condition bits are reversed!
// 01234 (docs)
// 43210 (real)
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
// The docs say always, though...
if (i.B.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
}
Value* ctr_ok = NULL;
if (XESELECTBITS(i.B.BO, 2, 2)) {
// Ignore ctr.
} else {
// Decrement counter.
Value* ctr = g.ctr_value();
ctr = b.CreateSub(ctr, b.getInt64(1));
g.update_ctr_value(ctr);
// Ctr check.
if (XESELECTBITS(i.B.BO, 1, 1)) {
ctr_ok = b.CreateICmpEQ(ctr, b.getInt64(0));
} else {
ctr_ok = b.CreateICmpNE(ctr, b.getInt64(0));
}
}
Value* cond_ok = NULL;
if (XESELECTBITS(i.B.BO, 4, 4)) {
// Ignore cond.
} else {
Value* cr = g.cr_value(i.B.BI >> 2);
cr = b.CreateAnd(cr, 1 << (i.B.BI & 3));
if (XESELECTBITS(i.B.BO, 3, 3)) {
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
} else {
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
}
}
// We do a bit of optimization here to make the llvm assembly easier to read.
Value* ok = NULL;
if (ctr_ok && cond_ok) {
ok = b.CreateAnd(ctr_ok, cond_ok);
} else if (ctr_ok) {
ok = ctr_ok;
} else if (cond_ok) {
ok = cond_ok;
}
// Handle unconditional branches without extra fluff.
BasicBlock* original_bb = b.GetInsertBlock();
if (ok) {
char name[32];
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
BasicBlock* next_block = g.GetNextBasicBlock();
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
next_block);
b.CreateCondBr(ok, branch_bb, next_block);
b.SetInsertPoint(branch_bb);
}
// Note that this occurs entirely within the branch true block.
uint32_t nia;
if (i.B.AA) {
nia = XEEXTS26(i.B.BD << 2);
} else {
nia = i.address + XEEXTS26(i.B.BD << 2);
}
if (XeEmitBranchTo(g, b, "bcx", i.address, i.B.LK)) {
return 1;
}
b.SetInsertPoint(original_bb);
return 0;
}
XEDISASMR(bcctrx, 0x4C000420, XL )(InstrData& i, InstrDisasm& d) {
// TODO(benvanik): mnemonics
d.Init("bcctr", "Branch Conditional to Count Register",
i.XL.LK ? InstrDisasm::kLR : 0);
if (!XESELECTBITS(i.XL.BO, 4, 4)) {
d.AddCR(i.XL.BI >> 2, InstrRegister::kRead);
}
d.AddUImmOperand(i.XL.BO, 1);
d.AddUImmOperand(i.XL.BI, 1);
d.AddCTR(InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
// if cond_ok then
// NIA <- CTR[0:61] || 0b00
// if LK then
// LR <- CIA + 4
// NOTE: the condition bits are reversed!
// 01234 (docs)
// 43210 (real)
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
// The docs say always, though...
if (i.XL.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
}
Value* cond_ok = NULL;
if (XESELECTBITS(i.XL.BO, 4, 4)) {
// Ignore cond.
} else {
Value* cr = g.cr_value(i.XL.BI >> 2);
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
if (XESELECTBITS(i.XL.BO, 3, 3)) {
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
} else {
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
}
}
// We do a bit of optimization here to make the llvm assembly easier to read.
Value* ok = NULL;
if (cond_ok) {
ok = cond_ok;
}
// Handle unconditional branches without extra fluff.
BasicBlock* original_bb = b.GetInsertBlock();
if (ok) {
char name[32];
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcctrx", i.address);
BasicBlock* next_block = g.GetNextBasicBlock();
XEASSERTNOTNULL(next_block);
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
next_block);
b.CreateCondBr(ok, branch_bb, next_block);
b.SetInsertPoint(branch_bb);
}
// Note that this occurs entirely within the branch true block.
if (XeEmitBranchTo(g, b, "bcctrx", i.address, i.XL.LK)) {
return 1;
}
b.SetInsertPoint(original_bb);
return 0;
}
XEDISASMR(bclrx, 0x4C000020, XL )(InstrData& i, InstrDisasm& d) {
std::string name = "bclr";
if (i.code == 0x4E800020) {
name = "blr";
}
d.Init(name, "Branch Conditional to Link Register",
i.XL.LK ? InstrDisasm::kLR : 0);
if (!XESELECTBITS(i.B.BO, 2, 2)) {
d.AddCTR(InstrRegister::kReadWrite);
}
if (!XESELECTBITS(i.B.BO, 4, 4)) {
d.AddCR(i.B.BI >> 2, InstrRegister::kRead);
}
d.AddUImmOperand(i.XL.BO, 1);
d.AddUImmOperand(i.XL.BI, 1);
d.AddLR(InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// if ¬BO[2] then
// CTR <- CTR - 1
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
// if ctr_ok & cond_ok then
// NIA <- LR[0:61] || 0b00
// if LK then
// LR <- CIA + 4
// NOTE: the condition bits are reversed!
// 01234 (docs)
// 43210 (real)
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
// The docs say always, though...
if (i.XL.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
}
Value* ctr_ok = NULL;
if (XESELECTBITS(i.XL.BO, 2, 2)) {
// Ignore ctr.
} else {
// Decrement counter.
Value* ctr = g.ctr_value();
ctr = b.CreateSub(ctr, b.getInt64(1));
// Ctr check.
if (XESELECTBITS(i.XL.BO, 1, 1)) {
ctr_ok = b.CreateICmpEQ(ctr, b.getInt64(0));
} else {
ctr_ok = b.CreateICmpNE(ctr, b.getInt64(0));
}
}
Value* cond_ok = NULL;
if (XESELECTBITS(i.XL.BO, 4, 4)) {
// Ignore cond.
} else {
Value* cr = g.cr_value(i.XL.BI >> 2);
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
if (XESELECTBITS(i.XL.BO, 3, 3)) {
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
} else {
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
}
}
// We do a bit of optimization here to make the llvm assembly easier to read.
Value* ok = NULL;
if (ctr_ok && cond_ok) {
ok = b.CreateAnd(ctr_ok, cond_ok);
} else if (ctr_ok) {
ok = ctr_ok;
} else if (cond_ok) {
ok = cond_ok;
}
// Handle unconditional branches without extra fluff.
BasicBlock* original_bb = b.GetInsertBlock();
if (ok) {
char name[32];
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bclrx", i.address);
BasicBlock* next_block = g.GetNextBasicBlock();
XEASSERTNOTNULL(next_block);
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
next_block);
b.CreateCondBr(ok, branch_bb, next_block);
b.SetInsertPoint(branch_bb);
}
// Note that this occurs entirely within the branch true block.
if (XeEmitBranchTo(g, b, "bclrx", i.address, i.XL.LK)) {
return 1;
}
b.SetInsertPoint(original_bb);
return 0;
}
// Condition register logical (A-23)
XEEMITTER(crand, 0x4C000202, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crandc, 0x4C000102, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(creqv, 0x4C000242, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crnand, 0x4C0001C2, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crnor, 0x4C000042, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(cror, 0x4C000382, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crorc, 0x4C000342, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crxor, 0x4C000182, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
// System linkage (A-24)
XEEMITTER(sc, 0x44000002, SC )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
// Trap (A-25)
int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
Value* va, Value* vb, uint32_t TO) {
// if (a < b) & TO[0] then TRAP
// if (a > b) & TO[1] then TRAP
// if (a = b) & TO[2] then TRAP
// if (a <u b) & TO[3] then TRAP
// if (a >u b) & TO[4] then TRAP
// Bits swapped:
// 01234
// 43210
if (!TO) {
return 0;
}
BasicBlock* after_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
g.GetNextBasicBlock());
BasicBlock* trap_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
after_bb);
// Create the basic blocks (so we can chain).
std::vector<BasicBlock*> bbs;
if (TO & (1 << 4)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
}
if (TO & (1 << 3)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
}
if (TO & (1 << 2)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
}
if (TO & (1 << 1)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
}
if (TO & (1 << 0)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
}
bbs.push_back(after_bb);
// Jump to the first bb.
b.CreateBr(bbs.front());
// Setup each basic block.
std::vector<BasicBlock*>::iterator it = bbs.begin();
if (TO & (1 << 4)) {
// a < b
BasicBlock* bb = *(it++);
b.SetInsertPoint(bb);
Value* cmp = b.CreateICmpSLT(va, vb);
b.CreateCondBr(cmp, trap_bb, *it);
}
if (TO & (1 << 3)) {
// a > b
BasicBlock* bb = *(it++);
b.SetInsertPoint(bb);
Value* cmp = b.CreateICmpSGT(va, vb);
b.CreateCondBr(cmp, trap_bb, *it);
}
if (TO & (1 << 2)) {
// a = b
BasicBlock* bb = *(it++);
b.SetInsertPoint(bb);
Value* cmp = b.CreateICmpEQ(va, vb);
b.CreateCondBr(cmp, trap_bb, *it);
}
if (TO & (1 << 1)) {
// a <u b
BasicBlock* bb = *(it++);
b.SetInsertPoint(bb);
Value* cmp = b.CreateICmpULT(va, vb);
b.CreateCondBr(cmp, trap_bb, *it);
}
if (TO & (1 << 0)) {
// a >u b
BasicBlock* bb = *(it++);
b.SetInsertPoint(bb);
Value* cmp = b.CreateICmpUGT(va, vb);
b.CreateCondBr(cmp, trap_bb, *it);
}
// Create trap BB.
b.SetInsertPoint(trap_bb);
g.SpillRegisters();
// TODO(benvanik): use @llvm.debugtrap? could make debugging better
b.CreateCall2(g.gen_module()->getFunction("XeTrap"),
g.gen_fn()->arg_begin(),
b.getInt32(i.address));
b.CreateBr(after_bb);
// Resume.
b.SetInsertPoint(after_bb);
return 0;
}
XEDISASMR(td, 0x7C000088, X )(InstrData& i, InstrDisasm& d) {
d.Init("td", "Trap Doubleword", 0);
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(td, 0x7C000088, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// a <- (RA)
// b <- (RB)
// if (a < b) & TO[0] then TRAP
// if (a > b) & TO[1] then TRAP
// if (a = b) & TO[2] then TRAP
// if (a <u b) & TO[3] then TRAP
// if (a >u b) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
g.gpr_value(i.X.RA),
g.gpr_value(i.X.RB),
i.X.RT);
}
XEDISASMR(tdi, 0x08000000, D )(InstrData& i, InstrDisasm& d) {
d.Init("tdi", "Trap Doubleword Immediate", 0);
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(tdi, 0x08000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// a <- (RA)
// if (a < EXTS(SI)) & TO[0] then TRAP
// if (a > EXTS(SI)) & TO[1] then TRAP
// if (a = EXTS(SI)) & TO[2] then TRAP
// if (a <u EXTS(SI)) & TO[3] then TRAP
// if (a >u EXTS(SI)) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
g.gpr_value(i.D.RA),
b.getInt64(XEEXTS16(i.D.DS)),
i.D.RT);
}
XEDISASMR(tw, 0x7C000008, X )(InstrData& i, InstrDisasm& d) {
d.Init("tw", "Trap Word", 0);
d.AddRegOperand(InstrRegister::kGPR, i.X.RA, InstrRegister::kRead);
d.AddRegOperand(InstrRegister::kGPR, i.X.RB, InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(tw, 0x7C000008, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// a <- EXTS((RA)[32:63])
// b <- EXTS((RB)[32:63])
// if (a < b) & TO[0] then TRAP
// if (a > b) & TO[1] then TRAP
// if (a = b) & TO[2] then TRAP
// if (a <u b) & TO[3] then TRAP
// if (a >u b) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RA),
b.getInt32Ty()),
b.getInt64Ty()),
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RB),
b.getInt32Ty()),
b.getInt64Ty()),
i.X.RT);
}
XEDISASMR(twi, 0x0C000000, D )(InstrData& i, InstrDisasm& d) {
d.Init("twi", "Trap Word Immediate", 0);
d.AddRegOperand(InstrRegister::kGPR, i.D.RA, InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(twi, 0x0C000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// a <- EXTS((RA)[32:63])
// if (a < EXTS(SI)) & TO[0] then TRAP
// if (a > EXTS(SI)) & TO[1] then TRAP
// if (a = EXTS(SI)) & TO[2] then TRAP
// if (a <u EXTS(SI)) & TO[3] then TRAP
// if (a >u EXTS(SI)) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.D.RA),
b.getInt32Ty()),
b.getInt64Ty()),
b.getInt64(XEEXTS16(i.D.DS)),
i.D.RT);
}
// Processor control (A-26)
XEEMITTER(mfcr, 0x7C000026, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEDISASMR(mfspr, 0x7C0002A6, XFX)(InstrData& i, InstrDisasm& d) {
d.Init("mfspr", "Move From Special Purpose Register", 0);
d.AddRegOperand(InstrRegister::kGPR, i.XFX.RT, InstrRegister::kWrite);
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
switch (n) {
case 1:
d.AddRegOperand(InstrRegister::kXER, 0, InstrRegister::kRead);
break;
case 8:
d.AddRegOperand(InstrRegister::kLR, 0, InstrRegister::kRead);
break;
case 9:
d.AddRegOperand(InstrRegister::kCTR, 0, InstrRegister::kRead);
break;
}
return d.Finish();
}
XEEMITTER(mfspr, 0x7C0002A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// n <- spr[5:9] || spr[0:4]
// if length(SPR(n)) = 64 then
// RT <- SPR(n)
// else
// RT <- i32.0 || SPR(n)
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
Value* v = NULL;
switch (n) {
case 1:
// XER
v = g.xer_value();
break;
case 8:
// LR
v = g.lr_value();
break;
case 9:
// CTR
v = g.ctr_value();
break;
default:
XEINSTRNOTIMPLEMENTED();
return 1;
}
g.update_gpr_value(i.XFX.RT, v);
return 0;
}
XEEMITTER(mftb, 0x7C0002E6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtcrf, 0x7C000120, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEDISASMR(mtspr, 0x7C0003A6, XFX)(InstrData& i, InstrDisasm& d) {
d.Init("mtspr", "Move To Special Purpose Register", 0);
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
switch (n) {
case 1:
d.AddRegOperand(InstrRegister::kXER, 0, InstrRegister::kWrite);
break;
case 8:
d.AddRegOperand(InstrRegister::kLR, 0, InstrRegister::kWrite);
break;
case 9:
d.AddRegOperand(InstrRegister::kCTR, 0, InstrRegister::kWrite);
break;
}
d.AddRegOperand(InstrRegister::kGPR, i.XFX.RT, InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(mtspr, 0x7C0003A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// n <- spr[5:9] || spr[0:4]
// if length(SPR(n)) = 64 then
// SPR(n) <- (RS)
// else
// SPR(n) <- (RS)[32:63]
Value* v = g.gpr_value(i.XFX.RT);
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
switch (n) {
case 1:
// XER
g.update_xer_value(v);
break;
case 8:
// LR
g.update_lr_value(v);
break;
case 9:
// CTR
g.update_ctr_value(v);
break;
default:
XEINSTRNOTIMPLEMENTED();
return 1;
}
return 0;
}
void RegisterEmitCategoryControl() {
XEREGISTERINSTR(bx, 0x48000000);
XEREGISTERINSTR(bcx, 0x40000000);
XEREGISTERINSTR(bcctrx, 0x4C000420);
XEREGISTERINSTR(bclrx, 0x4C000020);
XEREGISTEREMITTER(crand, 0x4C000202);
XEREGISTEREMITTER(crandc, 0x4C000102);
XEREGISTEREMITTER(creqv, 0x4C000242);
XEREGISTEREMITTER(crnand, 0x4C0001C2);
XEREGISTEREMITTER(crnor, 0x4C000042);
XEREGISTEREMITTER(cror, 0x4C000382);
XEREGISTEREMITTER(crorc, 0x4C000342);
XEREGISTEREMITTER(crxor, 0x4C000182);
XEREGISTEREMITTER(mcrf, 0x4C000000);
XEREGISTEREMITTER(sc, 0x44000002);
XEREGISTERINSTR(td, 0x7C000088);
XEREGISTERINSTR(tdi, 0x08000000);
XEREGISTERINSTR(tw, 0x7C000008);
XEREGISTERINSTR(twi, 0x0C000000);
XEREGISTEREMITTER(mfcr, 0x7C000026);
XEREGISTERINSTR(mfspr, 0x7C0002A6);
XEREGISTEREMITTER(mftb, 0x7C0002E6);
XEREGISTEREMITTER(mtcrf, 0x7C000120);
XEREGISTERINSTR(mtspr, 0x7C0003A6);
}
} // namespace codegen
} // namespace cpu
} // namespace xe

View File

@@ -0,0 +1,304 @@
/*
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/codegen/emit.h>
#include <xenia/cpu/codegen/function_generator.h>
using namespace llvm;
using namespace xe::cpu::codegen;
using namespace xe::cpu::ppc;
namespace xe {
namespace cpu {
namespace codegen {
// Floating-point arithmetic (A-8)
XEEMITTER(faddx, 0xFC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(faddsx, 0xEC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fdivx, 0xFC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fdivsx, 0xEC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmulx, 0xFC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmulsx, 0xEC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fresx, 0xEC000030, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(frsqrtex, 0xFC000034, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsubx, 0xFC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsubsx, 0xEC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fselx, 0xFC00002E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsqrtx, 0xFC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
// Floating-point multiply-add (A-9)
XEEMITTER(fmaddx, 0xFC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmaddsx, 0xEC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmsubx, 0xFC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmsubsx, 0xEC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmaddx, 0xFC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmaddsx, 0xEC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmsubx, 0xFC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
// Floating-point rounding and conversion (A-10)
XEEMITTER(fcfidx, 0xFC00069C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctidx, 0xFC00065C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctidzx, 0xFC00065E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctiwx, 0xFC00001C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctiwzx, 0xFC00001E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
// Floating-point compare (A-11)
XEEMITTER(fcmpo, 0xFC000040, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEDISASMR(fcmpu, 0xFC000000, X )(InstrData& i, InstrDisasm& d) {
d.Init("fcmpu", "Floating Compare Unordered",
(i.XO.OE ? InstrDisasm::kOE : 0) | (i.XO.Rc ? InstrDisasm::kRc : 0));
d.AddRegOperand(InstrRegister::kGPR, i.XO.RT, InstrRegister::kWrite);
d.AddRegOperand(InstrRegister::kGPR, i.XO.RA, InstrRegister::kRead);
d.AddRegOperand(InstrRegister::kGPR, i.XO.RB, InstrRegister::kRead);
return d.Finish();
}
XEEMITTER(fcmpu, 0xFC000000, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
// if (FRA) is a NaN or (FRB) is a NaN then
// c <- 0b0001
// else if (FRA) < (FRB) then
// c <- 0b1000
// else if (FRA) > (FRB) then
// c <- 0b0100
// else {
// c <- 0b0010
// }
// FPCC <- c
// CR[4*BF:4*BF+3] <- c
// if (FRA) is an SNaN or (FRB) is an SNaN then
// VXSNAN <- 1
XEINSTRNOTIMPLEMENTED();
return 1;
}
// Floating-point status and control register (A
XEEMITTER(mcrfs, 0xFC000080, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mffsx, 0xFC00048E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsb0x, 0xFC00008C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsb1x, 0xFC00004C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
// Floating-point move (A-21)
XEEMITTER(fabsx, 0xFC000210, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmrx, 0xFC000090, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnabsx, 0xFC000110, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnegx, 0xFC000050, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
void RegisterEmitCategoryFPU() {
XEREGISTEREMITTER(faddx, 0xFC00002A);
XEREGISTEREMITTER(faddsx, 0xEC00002A);
XEREGISTEREMITTER(fdivx, 0xFC000024);
XEREGISTEREMITTER(fdivsx, 0xEC000024);
XEREGISTEREMITTER(fmulx, 0xFC000032);
XEREGISTEREMITTER(fmulsx, 0xEC000032);
XEREGISTEREMITTER(fresx, 0xEC000030);
XEREGISTEREMITTER(frsqrtex, 0xFC000034);
XEREGISTEREMITTER(fsubx, 0xFC000028);
XEREGISTEREMITTER(fsubsx, 0xEC000028);
XEREGISTEREMITTER(fselx, 0xFC00002E);
XEREGISTEREMITTER(fsqrtx, 0xFC00002C);
XEREGISTEREMITTER(fsqrtsx, 0xEC00002C);
XEREGISTEREMITTER(fmaddx, 0xFC00003A);
XEREGISTEREMITTER(fmaddsx, 0xEC00003A);
XEREGISTEREMITTER(fmsubx, 0xFC000038);
XEREGISTEREMITTER(fmsubsx, 0xEC000038);
XEREGISTEREMITTER(fnmaddx, 0xFC00003E);
XEREGISTEREMITTER(fnmaddsx, 0xEC00003E);
XEREGISTEREMITTER(fnmsubx, 0xFC00003C);
XEREGISTEREMITTER(fnmsubsx, 0xEC00003C);
XEREGISTEREMITTER(fcfidx, 0xFC00069C);
XEREGISTEREMITTER(fctidx, 0xFC00065C);
XEREGISTEREMITTER(fctidzx, 0xFC00065E);
XEREGISTEREMITTER(fctiwx, 0xFC00001C);
XEREGISTEREMITTER(fctiwzx, 0xFC00001E);
XEREGISTEREMITTER(frspx, 0xFC000018);
XEREGISTEREMITTER(fcmpo, 0xFC000040);
XEREGISTEREMITTER(fcmpu, 0xFC000000);
XEREGISTEREMITTER(mcrfs, 0xFC000080);
XEREGISTEREMITTER(mffsx, 0xFC00048E);
XEREGISTEREMITTER(mtfsb0x, 0xFC00008C);
XEREGISTEREMITTER(mtfsb1x, 0xFC00004C);
XEREGISTEREMITTER(mtfsfx, 0xFC00058E);
XEREGISTEREMITTER(mtfsfix, 0xFC00010C);
XEREGISTEREMITTER(fabsx, 0xFC000210);
XEREGISTEREMITTER(fmrx, 0xFC000090);
XEREGISTEREMITTER(fnabsx, 0xFC000110);
XEREGISTEREMITTER(fnegx, 0xFC000050);
}
} // namespace codegen
} // namespace cpu
} // namespace xe

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,144 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
#define XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
#include <llvm/IR/Attributes.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <xenia/cpu/sdb.h>
#include <xenia/cpu/ppc/instr.h>
namespace xe {
namespace cpu {
namespace codegen {
class FunctionGenerator {
public:
FunctionGenerator(
xe_memory_ref memory, sdb::SymbolDatabase* sdb, sdb::FunctionSymbol* fn,
llvm::LLVMContext* context, llvm::Module* gen_module,
llvm::Function* gen_fn);
~FunctionGenerator();
sdb::SymbolDatabase* sdb();
sdb::FunctionSymbol* fn();
llvm::LLVMContext* context();
llvm::Module* gen_module();
llvm::Function* gen_fn();
sdb::FunctionBlock* fn_block();
void PushInsertPoint();
void PopInsertPoint();
void GenerateBasicBlocks();
llvm::BasicBlock* GetBasicBlock(uint32_t address);
llvm::BasicBlock* GetNextBasicBlock();
llvm::BasicBlock* GetReturnBasicBlock();
llvm::Function* GetFunction(sdb::FunctionSymbol* fn);
int GenerateIndirectionBranch(uint32_t cia, llvm::Value* target,
bool lk, bool likely_local);
llvm::Value* LoadStateValue(uint32_t offset, llvm::Type* type,
const char* name = "");
void StoreStateValue(uint32_t offset, llvm::Type* type, llvm::Value* value);
llvm::Value* cia_value();
llvm::Value* SetupLocal(llvm::Type* type, const char* name);
void FillRegisters();
void SpillRegisters();
llvm::Value* xer_value();
void update_xer_value(llvm::Value* value);
void update_xer_with_overflow(llvm::Value* value);
void update_xer_with_carry(llvm::Value* value);
void update_xer_with_overflow_and_carry(llvm::Value* value);
llvm::Value* lr_value();
void update_lr_value(llvm::Value* value);
llvm::Value* ctr_value();
void update_ctr_value(llvm::Value* value);
llvm::Value* cr_value(uint32_t n);
void update_cr_value(uint32_t n, llvm::Value* value);
void update_cr_with_cond(uint32_t n, llvm::Value* lhs, llvm::Value* rhs,
bool is_signed);
llvm::Value* gpr_value(uint32_t n);
void update_gpr_value(uint32_t n, llvm::Value* value);
llvm::Value* fpr_value(uint32_t n);
void update_fpr_value(uint32_t n, llvm::Value* value);
llvm::Value* GetMembase();
llvm::Value* GetMemoryAddress(uint32_t cia, llvm::Value* addr);
llvm::Value* ReadMemory(
uint32_t cia, llvm::Value* addr, uint32_t size, bool acquire = false);
void WriteMemory(
uint32_t cia, llvm::Value* addr, uint32_t size, llvm::Value* value,
bool release = false);
private:
void GenerateSharedBlocks();
int PrepareBasicBlock(sdb::FunctionBlock* block);
void GenerateBasicBlock(sdb::FunctionBlock* block);
void SetupLocals();
xe_memory_ref memory_;
sdb::SymbolDatabase* sdb_;
sdb::FunctionSymbol* fn_;
llvm::LLVMContext* context_;
llvm::Module* gen_module_;
llvm::Function* gen_fn_;
sdb::FunctionBlock* fn_block_;
llvm::BasicBlock* return_block_;
llvm::BasicBlock* internal_indirection_block_;
llvm::BasicBlock* external_indirection_block_;
llvm::BasicBlock* bb_;
llvm::IRBuilder<>* builder_;
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock::iterator> >
insert_points_;
std::map<uint32_t, llvm::BasicBlock*> bbs_;
// Address of the instruction being generated.
uint32_t cia_;
ppc::InstrAccessBits access_bits_;
struct {
llvm::Value* indirection_target;
llvm::Value* indirection_cia;
llvm::Value* xer;
llvm::Value* lr;
llvm::Value* ctr;
llvm::Value* cr[8];
llvm::Value* gpr[32];
llvm::Value* fpr[32];
} locals_;
};
} // namespace codegen
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_

View File

@@ -0,0 +1,335 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/codegen/module_generator.h>
#include <llvm/DIBuilder.h>
#include <llvm/Linker.h>
#include <llvm/PassManager.h>
#include <llvm/DebugInfo.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/IR/Attributes.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <xenia/cpu/cpu-private.h>
#include <xenia/cpu/ppc.h>
#include <xenia/cpu/codegen/function_generator.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::codegen;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
ModuleGenerator::ModuleGenerator(
xe_memory_ref memory, ExportResolver* export_resolver,
const char* module_name, const char* module_path, SymbolDatabase* sdb,
LLVMContext* context, Module* gen_module, ExecutionEngine* engine) {
memory_ = xe_memory_retain(memory);
export_resolver_ = export_resolver;
module_name_ = xestrdupa(module_name);
module_path_ = xestrdupa(module_path);
sdb_ = sdb;
context_ = context;
gen_module_ = gen_module;
engine_ = engine;
di_builder_ = NULL;
}
ModuleGenerator::~ModuleGenerator() {
for (std::map<uint32_t, CodegenFunction*>::iterator it =
functions_.begin(); it != functions_.end(); ++it) {
delete it->second;
}
delete di_builder_;
xe_free(module_path_);
xe_free(module_name_);
xe_memory_release(memory_);
}
int ModuleGenerator::Generate() {
std::string error_message;
// Setup a debug info builder.
// This is used when creating any debug info. We may want to go more
// fine grained than this, but for now it's something.
char dir[XE_MAX_PATH];
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path_));
char* slash = xestrrchra(dir, '/');
if (slash) {
*(slash + 1) = 0;
}
di_builder_ = new DIBuilder(*gen_module_);
di_builder_->createCompileUnit(
dwarf::DW_LANG_C99, //0x8010,
StringRef(module_name_),
StringRef(dir),
StringRef("xenia"),
true,
StringRef(""),
0);
cu_ = (MDNode*)di_builder_->getCU();
// Add export wrappers.
//
// Add all functions.
// We do two passes - the first creates the function signature and global
// value (so that we can call it), the second actually builds the function.
std::vector<FunctionSymbol*> functions;
if (!sdb_->GetAllFunctions(functions)) {
XELOGI(XT("Beginning prep of %ld functions..."), functions.size());
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
it != functions.end(); ++it) {
FunctionSymbol* fn = *it;
switch (fn->type) {
case FunctionSymbol::User:
PrepareFunction(fn);
break;
case FunctionSymbol::Kernel:
if (fn->kernel_export && fn->kernel_export->is_implemented) {
AddPresentImport(fn);
} else {
AddMissingImport(fn);
}
break;
default:
XEASSERTALWAYS();
break;
}
}
XELOGI(XT("Function prep complete"));
}
// Build out all the user functions.
size_t n = 0;
XELOGI(XT("Beginning generation of %ld functions..."), functions.size());
for (std::map<uint32_t, CodegenFunction*>::iterator it =
functions_.begin(); it != functions_.end(); ++it, ++n) {
FunctionSymbol* symbol = it->second->symbol;
XELOGI(XT("Generating %ld/%ld %.8X %s"),
n, functions_.size(), symbol->start_address, symbol->name());
BuildFunction(it->second);
}
XELOGI(XT("Function generation complete"));
di_builder_->finalize();
return 0;
}
void ModuleGenerator::AddFunctionsToMap(
std::tr1::unordered_map<uint32_t, llvm::Function*>& map) {
for (std::map<uint32_t, CodegenFunction*>::iterator it = functions_.begin();
it != functions_.end(); ++it) {
map.insert(std::pair<uint32_t, Function*>(it->first, it->second->function));
}
}
ModuleGenerator::CodegenFunction* ModuleGenerator::GetCodegenFunction(
uint32_t address) {
std::map<uint32_t, CodegenFunction*>::iterator it = functions_.find(address);
if (it != functions_.end()) {
return it->second;
}
return NULL;
}
Function* ModuleGenerator::CreateFunctionDefinition(const char* name) {
Module* m = gen_module_;
LLVMContext& context = m->getContext();
std::vector<Type*> args;
args.push_back(PointerType::getUnqual(Type::getInt8Ty(context)));
args.push_back(Type::getInt64Ty(context));
Type* return_type = Type::getVoidTy(context);
FunctionType* ft = FunctionType::get(return_type,
ArrayRef<Type*>(args), false);
Function* f = cast<Function>(m->getOrInsertFunction(
StringRef(name), ft));
f->setVisibility(GlobalValue::DefaultVisibility);
// Indicate that the function will never be unwound with an exception.
// If we ever support native exception handling we may need to remove this.
f->doesNotThrow();
// May be worth trying the X86_FastCall, as we only need state in a register.
//f->setCallingConv(CallingConv::Fast);
f->setCallingConv(CallingConv::C);
Function::arg_iterator fn_args = f->arg_begin();
// 'state'
Value* fn_arg = fn_args++;
fn_arg->setName("state");
f->setDoesNotAlias(1);
f->setDoesNotCapture(1);
// 'state' should try to be in a register, if possible.
// TODO(benvanik): verify that's a good idea.
// f->getArgumentList().begin()->addAttr(
// Attribute::get(context, AttrBuilder().addAttribute(Attribute::InReg)));
// 'lr'
fn_arg = fn_args++;
fn_arg->setName("lr");
return f;
};
void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
Module *m = gen_module_;
LLVMContext& context = m->getContext();
// Create the function (and setup args/attributes/etc).
Function* f = CreateFunctionDefinition(fn->name());
BasicBlock* block = BasicBlock::Create(context, "entry", f);
IRBuilder<> b(block);
if (FLAGS_trace_kernel_calls) {
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
b.CreateCall4(
traceKernelCall,
f->arg_begin(),
b.getInt64(fn->start_address),
++f->arg_begin(),
b.getInt64((uint64_t)fn->kernel_export));
}
b.CreateRetVoid();
OptimizeFunction(m, f);
//GlobalAlias *alias = new GlobalAlias(f->getType(), GlobalValue::InternalLinkage, name, f, m);
// printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
// info->value_address, info->thunk_address, info->ordinal,
// info->ordinal, implemented ? " " : "!!", name);
// For values:
// printf(" V %.8X %.3X (%3d) %s %s\n",
// info->value_address, info->ordinal, info->ordinal,
// implemented ? " " : "!!", name);
}
void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
Module *m = gen_module_;
LLVMContext& context = m->getContext();
const DataLayout* dl = engine_->getDataLayout();
Type* intPtrTy = dl->getIntPtrType(context);
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
// Add the externs.
// We have both the shim function pointer and the shim data pointer.
char shim_name[256];
xesnprintfa(shim_name, XECOUNT(shim_name),
"__shim_%s", fn->kernel_export->name);
char shim_data_name[256];
xesnprintfa(shim_data_name, XECOUNT(shim_data_name),
"__shim_data_%s", fn->kernel_export->name);
std::vector<Type*> shimArgs;
shimArgs.push_back(int8PtrTy);
shimArgs.push_back(int8PtrTy);
FunctionType* shimTy = FunctionType::get(
Type::getVoidTy(context), shimArgs, false);
Function* shim = Function::Create(
shimTy, Function::ExternalLinkage, shim_name, m);
GlobalVariable* gv = new GlobalVariable(
*m, int8PtrTy, true, GlobalValue::ExternalLinkage, 0,
shim_data_name);
// TODO(benvanik): don't initialize on startup - move to exec_module
gv->setInitializer(ConstantExpr::getIntToPtr(
ConstantInt::get(intPtrTy,
(uintptr_t)fn->kernel_export->function_data.shim_data),
int8PtrTy));
engine_->addGlobalMapping(shim,
(void*)fn->kernel_export->function_data.shim);
// Create the function (and setup args/attributes/etc).
Function* f = CreateFunctionDefinition(fn->name());
BasicBlock* block = BasicBlock::Create(context, "entry", f);
IRBuilder<> b(block);
if (FLAGS_trace_kernel_calls) {
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
b.CreateCall4(
traceKernelCall,
f->arg_begin(),
b.getInt64(fn->start_address),
++f->arg_begin(),
b.getInt64((uint64_t)fn->kernel_export));
}
b.CreateCall2(
shim,
f->arg_begin(),
b.CreateLoad(gv));
b.CreateRetVoid();
OptimizeFunction(m, f);
}
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
// Create the function (and setup args/attributes/etc).
Function* f = CreateFunctionDefinition(fn->name());
// Setup our codegen wrapper to keep all the pointers together.
CodegenFunction* cgf = new CodegenFunction();
cgf->symbol = fn;
cgf->function_type = f->getFunctionType();
cgf->function = f;
functions_.insert(std::pair<uint32_t, CodegenFunction*>(
fn->start_address, cgf));
}
void ModuleGenerator::BuildFunction(CodegenFunction* cgf) {
FunctionSymbol* fn = cgf->symbol;
// Setup the generation context.
FunctionGenerator fgen(
memory_, sdb_, fn, context_, gen_module_, cgf->function);
// Run through and generate each basic block.
fgen.GenerateBasicBlocks();
// Run the optimizer on the function.
// Doing this here keeps the size of the IR small and speeds up the later
// passes.
OptimizeFunction(gen_module_, cgf->function);
}
void ModuleGenerator::OptimizeFunction(Module* m, Function* fn) {
FunctionPassManager pm(m);
//fn->dump();
if (FLAGS_optimize_ir_functions) {
PassManagerBuilder pmb;
pmb.OptLevel = 3;
pmb.SizeLevel = 0;
pmb.Inliner = createFunctionInliningPass();
pmb.Vectorize = true;
pmb.LoopVectorize = true;
pmb.populateFunctionPassManager(pm);
}
pm.add(createVerifierPass());
pm.run(*fn);
}

View File

@@ -0,0 +1,91 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_
#define XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/cpu/sdb.h>
#include <xenia/core/memory.h>
#include <xenia/kernel/export.h>
namespace llvm {
class DIBuilder;
class ExecutionEngine;
class Function;
class FunctionType;
class LLVMContext;
class Module;
class MDNode;
}
namespace xe {
namespace cpu {
namespace codegen {
class ModuleGenerator {
public:
ModuleGenerator(
xe_memory_ref memory, kernel::ExportResolver* export_resolver,
const char* module_name, const char* module_path,
sdb::SymbolDatabase* sdb,
llvm::LLVMContext* context, llvm::Module* gen_module,
llvm::ExecutionEngine* engine);
~ModuleGenerator();
int Generate();
void AddFunctionsToMap(
std::tr1::unordered_map<uint32_t, llvm::Function*>& map);
private:
class CodegenFunction {
public:
sdb::FunctionSymbol* symbol;
llvm::FunctionType* function_type;
llvm::Function* function;
};
CodegenFunction* GetCodegenFunction(uint32_t address);
void AddImports();
llvm::Function* CreateFunctionDefinition(const char* name);
void AddMissingImport(sdb::FunctionSymbol* fn);
void AddPresentImport(sdb::FunctionSymbol* fn);
void PrepareFunction(sdb::FunctionSymbol* fn);
void BuildFunction(CodegenFunction* cgf);
void OptimizeFunction(llvm::Module* m, llvm::Function* fn);
xe_memory_ref memory_;
kernel::ExportResolver* export_resolver_;
char* module_name_;
char* module_path_;
sdb::SymbolDatabase* sdb_;
llvm::LLVMContext* context_;
llvm::Module* gen_module_;
llvm::ExecutionEngine* engine_;
llvm::DIBuilder* di_builder_;
llvm::MDNode* cu_;
std::map<uint32_t, CodegenFunction*> functions_;
};
} // namespace codegen
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_

View File

@@ -0,0 +1,14 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'emit.h',
'emit_alu.cc',
'emit_control.cc',
'emit_fpu.cc',
'emit_memory.cc',
'function_generator.cc',
'function_generator.h',
'module_generator.cc',
'module_generator.h',
],
}

View File

@@ -0,0 +1,30 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_PRIVATE_H_
#define XENIA_CPU_PRIVATE_H_
#include <gflags/gflags.h>
DECLARE_bool(trace_instructions);
DECLARE_bool(trace_user_calls);
DECLARE_bool(trace_kernel_calls);
DECLARE_string(load_module_map);
DECLARE_string(dump_path);
DECLARE_bool(dump_module_bitcode);
DECLARE_bool(dump_module_map);
DECLARE_bool(optimize_ir_modules);
DECLARE_bool(optimize_ir_functions);
#endif // XENIA_CPU_PRIVATE_H_

41
src/xenia/cpu/cpu.cc Normal file
View File

@@ -0,0 +1,41 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/cpu-private.h>
// Tracing:
DEFINE_bool(trace_instructions, false,
"Trace all instructions.");
DEFINE_bool(trace_user_calls, false,
"Trace all user function calls.");
DEFINE_bool(trace_kernel_calls, false,
"Trace all kernel function calls.");
// Debugging:
DEFINE_string(load_module_map, "",
"Loads a .map for symbol names and to diff with the generated symbol "
"database.");
// Dumping:
DEFINE_string(dump_path, "build/",
"Directory that dump files are placed into.");
DEFINE_bool(dump_module_bitcode, true,
"Writes the module bitcode both before and after optimizations.");
DEFINE_bool(dump_module_map, true,
"Dumps the module symbol database.");
// Optimizations:
DEFINE_bool(optimize_ir_modules, true,
"Whether to run LLVM optimizations on modules.");
DEFINE_bool(optimize_ir_functions, true,
"Whether to run LLVM optimizations on functions.");

15
src/xenia/cpu/cpu.h Normal file
View File

@@ -0,0 +1,15 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_CPU_H_
#define XENIA_CPU_CPU_H_
#include <xenia/cpu/processor.h>
#endif // XENIA_CPU_CPU_H_

View File

@@ -0,0 +1,326 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/exec_module.h>
#include <llvm/Linker.h>
#include <llvm/PassManager.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/system_error.h>
#include <llvm/Support/Threading.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <xenia/cpu/cpu-private.h>
#include <xenia/cpu/llvm_exports.h>
#include <xenia/cpu/sdb.h>
#include <xenia/cpu/codegen/module_generator.h>
#include <xenia/cpu/ppc/instr.h>
#include <xenia/cpu/ppc/state.h>
#include <xenia/cpu/xethunk/xethunk.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::codegen;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
ExecModule::ExecModule(
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
const char* module_name, const char* module_path,
shared_ptr<llvm::ExecutionEngine>& engine) {
memory_ = xe_memory_retain(memory);
export_resolver_ = export_resolver;
module_name_ = xestrdupa(module_name);
module_path_ = xestrdupa(module_path);
engine_ = engine;
context_ = shared_ptr<LLVMContext>(new LLVMContext());
}
ExecModule::~ExecModule() {
if (gen_module_) {
Uninit();
engine_->removeModule(gen_module_.get());
}
xe_free(module_path_);
xe_free(module_name_);
xe_memory_release(memory_);
}
int ExecModule::PrepareXex(xe_xex2_ref xex) {
sdb_ = shared_ptr<sdb::SymbolDatabase>(
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
int result_code = Prepare();
if (result_code) {
return result_code;
}
// Import variables.
// TODO??
return 0;
}
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
sdb_ = shared_ptr<sdb::SymbolDatabase>(
new sdb::RawSymbolDatabase(memory_, export_resolver_.get(),
start_address, end_address));
return Prepare();
}
int ExecModule::Prepare() {
int result_code = 1;
std::string error_message;
char file_name[XE_MAX_PATH];
OwningPtr<MemoryBuffer> shared_module_buffer;
auto_ptr<Module> shared_module;
auto_ptr<raw_ostream> outs;
PassManager pm;
PassManagerBuilder pmb;
// TODO(benvanik): embed the bc file into the emulator.
const char *thunk_path = "src/xenia/cpu/xethunk/xethunk.bc";
// Calculate a cache path based on the module, the CPU version, and other
// bits.
// TODO(benvanik): cache path calculation.
//const char *cache_path = "build/generated.bc";
// Check the cache to see if the bitcode exists.
// If it does, load that module directly. In the future we could also cache
// on linked binaries but that requires more safety around versioning.
// TODO(benvanik): check cache for module bitcode and load.
// if (path_exists(cache_key)) {
// exec_module = load_bitcode(cache_key);
// sdb = load_symbol_table(cache_key);
// }
// If not found in cache, generate a new module.
if (!gen_module_.get()) {
// Load shared bitcode files.
// These contain globals and common thunk code that are used by the
// generated code.
XEEXPECTZERO(MemoryBuffer::getFile(thunk_path, shared_module_buffer));
shared_module = auto_ptr<Module>(ParseBitcodeFile(
&*shared_module_buffer, *context_, &error_message));
XEEXPECTNOTNULL(shared_module.get());
// Analyze the module and add its symbols to the symbol database.
XEEXPECTZERO(sdb_->Analyze());
// Load a specified module map and diff.
if (FLAGS_load_module_map.size()) {
sdb_->ReadMap(FLAGS_load_module_map.c_str());
}
// Dump the symbol database.
if (FLAGS_dump_module_map) {
xesnprintfa(file_name, XECOUNT(file_name),
"%s%s.map", FLAGS_dump_path.c_str(), module_name_);
sdb_->WriteMap(file_name);
}
// Initialize the module.
gen_module_ = shared_ptr<Module>(
new Module(module_name_, *context_.get()));
// TODO(benavnik): addModuleFlag?
// Inject globals.
// This should be done ASAP to ensure that JITed functions can use the
// constant addresses.
XEEXPECTZERO(InjectGlobals());
// Link shared module into generated module.
// This gives us a single module that we can optimize and prevents the need
// for foreward declarations.
Linker::LinkModules(gen_module_.get(), shared_module.get(), 0,
&error_message);
// Build the module from the source code.
codegen_ = auto_ptr<ModuleGenerator>(new ModuleGenerator(
memory_, export_resolver_.get(), module_name_, module_path_,
sdb_.get(), context_.get(), gen_module_.get(),
engine_.get()));
XEEXPECTZERO(codegen_->Generate());
// Write to cache.
// TODO(benvanik): cache stuff
// Dump pre-optimized module to disk.
if (FLAGS_dump_module_bitcode) {
xesnprintfa(file_name, XECOUNT(file_name),
"%s%s-preopt.bc", FLAGS_dump_path.c_str(), module_name_);
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
file_name, error_message, raw_fd_ostream::F_Binary));
XEEXPECTTRUE(error_message.empty());
WriteBitcodeToFile(gen_module_.get(), *outs);
}
}
// Link optimizations.
XEEXPECTZERO(gen_module_->MaterializeAllPermanently(&error_message));
// Reset target triple (ignore what's in xethunk).
gen_module_->setTargetTriple(llvm::sys::getDefaultTargetTriple());
// Run full module optimizations.
pm.add(new DataLayout(gen_module_.get()));
if (FLAGS_optimize_ir_modules) {
pm.add(createVerifierPass());
pmb.OptLevel = 3;
pmb.SizeLevel = 0;
pmb.Inliner = createFunctionInliningPass();
pmb.Vectorize = true;
pmb.LoopVectorize = true;
pmb.populateModulePassManager(pm);
pmb.populateLTOPassManager(pm, false, true);
}
pm.add(createVerifierPass());
pm.run(*gen_module_);
// Dump post-optimized module to disk.
if (FLAGS_optimize_ir_modules && FLAGS_dump_module_bitcode) {
xesnprintfa(file_name, XECOUNT(file_name),
"%s%s.bc", FLAGS_dump_path.c_str(), module_name_);
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
file_name, error_message, raw_fd_ostream::F_Binary));
XEEXPECTTRUE(error_message.empty());
WriteBitcodeToFile(gen_module_.get(), *outs);
}
// TODO(benvanik): experiment with LLD to see if we can write out a dll.
// Initialize the module.
XEEXPECTZERO(Init());
// Force JIT of all functions.
// for (Module::iterator it = gen_module_->begin(); it != gen_module_->end();
// ++it) {
// Function* fn = it;
// if (!fn->isDeclaration()) {
// engine_->getPointerToFunction(fn);
// }
// }
result_code = 0;
XECLEANUP:
return result_code;
}
void ExecModule::AddFunctionsToMap(FunctionMap& map) {
codegen_->AddFunctionsToMap(map);
}
int ExecModule::InjectGlobals() {
LLVMContext& context = *context_.get();
const DataLayout* dl = engine_->getDataLayout();
Type* intPtrTy = dl->getIntPtrType(context);
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
GlobalVariable* gv;
// xe_memory_base
// This is the base void* pointer to the memory space.
gv = new GlobalVariable(
*gen_module_,
int8PtrTy,
true,
GlobalValue::ExternalLinkage,
0,
"xe_memory_base");
// Align to 64b - this makes SSE faster.
gv->setAlignment(64);
gv->setInitializer(ConstantExpr::getIntToPtr(
ConstantInt::get(intPtrTy, (uintptr_t)xe_memory_addr(memory_, 0)),
int8PtrTy));
SetupLlvmExports(gen_module_.get(), dl, engine_.get());
return 0;
}
int ExecModule::Init() {
// Setup all kernel variables.
std::vector<VariableSymbol*> variables;
if (sdb_->GetAllVariables(variables)) {
return 1;
}
uint8_t* mem = xe_memory_addr(memory_, 0);
for (std::vector<VariableSymbol*>::iterator it = variables.begin();
it != variables.end(); ++it) {
VariableSymbol* var = *it;
if (!var->kernel_export) {
continue;
}
KernelExport* kernel_export = var->kernel_export;
// Grab, if available.
uint32_t* slot = (uint32_t*)(mem + var->address);
if (kernel_export->type == KernelExport::Function) {
// Not exactly sure what this should be...
// TODO(benvanik): find out what import variables are.
} else {
if (kernel_export->is_implemented) {
// Implemented - replace with pointer.
*slot = XESWAP32BE(kernel_export->variable_ptr);
} else {
// Not implemented - write with a dummy value.
*slot = XESWAP32BE(0xDEADBEEF);
XELOGCPU(XT("WARNING: imported a variable with no value: %s"),
kernel_export->name);
}
}
}
// Run static initializers. I'm not sure we'll have any, but who knows.
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);
// Grab the init function and call it.
Function* xe_module_init = gen_module_->getFunction("xe_module_init");
std::vector<GenericValue> args;
GenericValue ret = engine_->runFunction(xe_module_init, args);
return static_cast<int>(ret.IntVal.getSExtValue());
}
int ExecModule::Uninit() {
// Grab function and call it.
Function* xe_module_uninit = gen_module_->getFunction("xe_module_uninit");
std::vector<GenericValue> args;
engine_->runFunction(xe_module_uninit, args);
// Run static destructors.
engine_->runStaticConstructorsDestructors(gen_module_.get(), true);
return 0;
}
void ExecModule::Dump() {
sdb_->Dump(stdout);
}

View File

@@ -0,0 +1,83 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_USERMODULE_H_
#define XENIA_CPU_USERMODULE_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/cpu/sdb.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/xex2.h>
namespace llvm {
class ExecutionEngine;
class Function;
class LLVMContext;
class Module;
}
namespace xe {
namespace cpu {
namespace codegen {
class ModuleGenerator;
}
}
}
namespace xe {
namespace cpu {
typedef std::tr1::unordered_map<uint32_t, llvm::Function*> FunctionMap;
class ExecModule {
public:
ExecModule(
xe_memory_ref memory, shared_ptr<kernel::ExportResolver> export_resolver,
const char* module_name, const char* module_path,
shared_ptr<llvm::ExecutionEngine>& engine);
~ExecModule();
int PrepareXex(xe_xex2_ref xex);
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
void AddFunctionsToMap(FunctionMap& map);
void Dump();
private:
int Prepare();
int InjectGlobals();
int Init();
int Uninit();
xe_memory_ref memory_;
shared_ptr<kernel::ExportResolver> export_resolver_;
char* module_name_;
char* module_path_;
shared_ptr<llvm::ExecutionEngine> engine_;
shared_ptr<sdb::SymbolDatabase> sdb_;
shared_ptr<llvm::LLVMContext> context_;
shared_ptr<llvm::Module> gen_module_;
auto_ptr<codegen::ModuleGenerator> codegen_;
FunctionMap fns_;
};
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_USERMODULE_H_

View File

@@ -0,0 +1,176 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/llvm_exports.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <xenia/cpu/sdb.h>
#include <xenia/cpu/ppc/instr.h>
#include <xenia/cpu/ppc/state.h>
#include <xenia/kernel/export.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
namespace {
void XeTrap(xe_ppc_state_t* state, uint32_t cia) {
XELOGE(XT("TRAP"));
XEASSERTALWAYS();
}
void XeIndirectBranch(xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
XELOGCPU(XT("INDIRECT BRANCH %.8X -> %.8X"),
(uint32_t)br_ia, (uint32_t)target);
XEASSERTALWAYS();
}
void XeInvalidInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
ppc::InstrData i;
i.address = cia;
i.code = data;
i.type = ppc::GetInstrType(i.code);
if (!i.type) {
XELOGCPU(XT("INVALID INSTRUCTION %.8X: %.8X ???"),
i.address, i.code);
} else if (i.type->disassemble) {
ppc::InstrDisasm d;
i.type->disassemble(i, d);
std::string disasm;
d.Dump(disasm);
XELOGCPU(XT("INVALID INSTRUCTION %.8X: %.8X %s"),
i.address, i.code, disasm.c_str());
} else {
XELOGCPU(XT("INVALID INSTRUCTION %.8X: %.8X %s"),
i.address, i.code, i.type->name);
}
}
void XeAccessViolation(xe_ppc_state_t* state, uint32_t cia, uint64_t ea) {
XELOGE(XT("INVALID ACCESS %.8X: tried to touch %.8X"),
cia, (uint32_t)ea);
XEASSERTALWAYS();
}
void XeTraceKernelCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
KernelExport* kernel_export) {
XELOGCPU(XT("TRACE: %.8X -> k.%.8X (%s)"),
(uint32_t)call_ia - 4, (uint32_t)cia,
kernel_export ? kernel_export->name : "unknown");
}
void XeTraceUserCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
FunctionSymbol* fn) {
XELOGCPU(XT("TRACE: %.8X -> u.%.8X (%s)"),
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
}
void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
ppc::InstrType* type = ppc::GetInstrType(data);
XELOGCPU(XT("TRACE: %.8X %.8X %s %s"),
cia, data,
type && type->emit ? " " : "X",
type ? type->name : "<unknown>");
if (cia == 0x82014468) {
printf("BREAKBREAKBREAK\n");
}
// TODO(benvanik): better disassembly, printing of current register values/etc
}
}
void xe::cpu::SetupLlvmExports(llvm::Module* module,
const llvm::DataLayout* dl,
llvm::ExecutionEngine* engine) {
LLVMContext& context = module->getContext();
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
// Control methods:
std::vector<Type*> trapArgs;
trapArgs.push_back(int8PtrTy);
trapArgs.push_back(Type::getInt32Ty(context));
FunctionType* trapTy = FunctionType::get(
Type::getVoidTy(context), trapArgs, false);
engine->addGlobalMapping(Function::Create(
trapTy, Function::ExternalLinkage, "XeTrap",
module), (void*)&XeTrap);
std::vector<Type*> indirectBranchArgs;
indirectBranchArgs.push_back(int8PtrTy);
indirectBranchArgs.push_back(Type::getInt64Ty(context));
indirectBranchArgs.push_back(Type::getInt64Ty(context));
FunctionType* indirectBranchTy = FunctionType::get(
Type::getVoidTy(context), indirectBranchArgs, false);
engine->addGlobalMapping(Function::Create(
indirectBranchTy, Function::ExternalLinkage, "XeIndirectBranch",
module), (void*)&XeIndirectBranch);
// Debugging methods:
std::vector<Type*> invalidInstructionArgs;
invalidInstructionArgs.push_back(int8PtrTy);
invalidInstructionArgs.push_back(Type::getInt32Ty(context));
invalidInstructionArgs.push_back(Type::getInt32Ty(context));
FunctionType* invalidInstructionTy = FunctionType::get(
Type::getVoidTy(context), invalidInstructionArgs, false);
engine->addGlobalMapping(Function::Create(
invalidInstructionTy, Function::ExternalLinkage, "XeInvalidInstruction",
module), (void*)&XeInvalidInstruction);
std::vector<Type*> accessViolationArgs;
accessViolationArgs.push_back(int8PtrTy);
accessViolationArgs.push_back(Type::getInt32Ty(context));
accessViolationArgs.push_back(Type::getInt64Ty(context));
FunctionType* accessViolationTy = FunctionType::get(
Type::getVoidTy(context), accessViolationArgs, false);
engine->addGlobalMapping(Function::Create(
accessViolationTy, Function::ExternalLinkage, "XeAccessViolation",
module), (void*)&XeAccessViolation);
// Tracing methods:
std::vector<Type*> traceCallArgs;
traceCallArgs.push_back(int8PtrTy);
traceCallArgs.push_back(Type::getInt64Ty(context));
traceCallArgs.push_back(Type::getInt64Ty(context));
traceCallArgs.push_back(Type::getInt64Ty(context));
FunctionType* traceCallTy = FunctionType::get(
Type::getVoidTy(context), traceCallArgs, false);
std::vector<Type*> traceInstructionArgs;
traceInstructionArgs.push_back(int8PtrTy);
traceInstructionArgs.push_back(Type::getInt32Ty(context));
traceInstructionArgs.push_back(Type::getInt32Ty(context));
FunctionType* traceInstructionTy = FunctionType::get(
Type::getVoidTy(context), traceInstructionArgs, false);
engine->addGlobalMapping(Function::Create(
traceCallTy, Function::ExternalLinkage, "XeTraceKernelCall",
module), (void*)&XeTraceKernelCall);
engine->addGlobalMapping(Function::Create(
traceCallTy, Function::ExternalLinkage, "XeTraceUserCall",
module), (void*)&XeTraceUserCall);
engine->addGlobalMapping(Function::Create(
traceInstructionTy, Function::ExternalLinkage, "XeTraceInstruction",
module), (void*)&XeTraceInstruction);
}

View File

@@ -0,0 +1,38 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_LLVM_EXPORTS_H_
#define XENIA_CPU_LLVM_EXPORTS_H_
#include <xenia/common.h>
#include <xenia/core.h>
namespace llvm {
class ExecutionEngine;
class LLVMContext;
class Module;
class DataLayout;
}
namespace xe {
namespace cpu {
void SetupLlvmExports(llvm::Module* module,
const llvm::DataLayout* dl,
llvm::ExecutionEngine* engine);
} // cpu
} // xe
#endif // XENIA_CPU_LLVM_EXPORTS_H_

18
src/xenia/cpu/ppc.h Normal file
View File

@@ -0,0 +1,18 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_PPC_H_
#define XENIA_CPU_PPC_H_
#include <xenia/common.h>
#include <xenia/cpu/ppc/instr.h>
#include <xenia/cpu/ppc/state.h>
#endif // XENIA_CPU_PPC_H_

407
src/xenia/cpu/ppc/instr.cc Normal file
View File

@@ -0,0 +1,407 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/ppc/instr.h>
#include <sstream>
#include <xenia/cpu/ppc/instr_tables.h>
using namespace xe::cpu::ppc;
void InstrAccessBits::Clear() {
spr = cr = gpr = fpr = 0;
}
void InstrAccessBits::Extend(InstrAccessBits& other) {
spr |= other.spr;
cr |= other.cr;
gpr |= other.gpr;
fpr |= other.fpr;
}
void InstrAccessBits::MarkAccess(InstrRegister& reg) {
uint64_t bits = 0;
if (reg.access & InstrRegister::kRead) {
bits |= 0x1;
}
if (reg.access & InstrRegister::kWrite) {
bits |= 0x2;
}
switch (reg.set) {
case InstrRegister::kXER:
spr |= bits << (2 * 0);
break;
case InstrRegister::kLR:
spr |= bits << (2 * 1);
break;
case InstrRegister::kCTR:
spr |= bits << (2 * 2);
break;
case InstrRegister::kCR:
cr |= bits << (2 * reg.ordinal);
break;
case InstrRegister::kFPSCR:
spr |= bits << (2 * 3);
break;
case InstrRegister::kGPR:
gpr |= bits << (2 * reg.ordinal);
break;
case InstrRegister::kFPR:
fpr |= bits << (2 * reg.ordinal);
break;
default:
case InstrRegister::kVMX:
XEASSERTALWAYS();
break;
}
}
void InstrAccessBits::Dump(std::string& out_str) {
std::stringstream str;
if (spr) {
uint64_t spr_t = spr;
if (spr_t & 0x3) {
str << "XER [";
str << ((spr_t & 1) ? "R" : " ");
str << ((spr_t & 2) ? "W" : " ");
str << "] ";
}
spr_t >>= 2;
if (spr_t & 0x3) {
str << "LR [";
str << ((spr_t & 1) ? "R" : " ");
str << ((spr_t & 2) ? "W" : " ");
str << "] ";
}
spr_t >>= 2;
if (spr_t & 0x3) {
str << "CTR [";
str << ((spr_t & 1) ? "R" : " ");
str << ((spr_t & 2) ? "W" : " ");
str << "] ";
}
spr_t >>= 2;
if (spr_t & 0x3) {
str << "FPCSR [";
str << ((spr_t & 1) ? "R" : " ");
str << ((spr_t & 2) ? "W" : " ");
str << "] ";
}
spr_t >>= 2;
}
if (cr) {
uint64_t cr_t = cr;
for (size_t n = 0; n < 8; n++) {
if (cr_t & 0x3) {
str << "cr" << n << " [";
str << ((cr_t & 1) ? "R" : " ");
str << ((cr_t & 2) ? "W" : " ");
str << "] ";
}
cr_t >>= 2;
}
}
if (gpr) {
uint64_t gpr_t = gpr;
for (size_t n = 0; n < 32; n++) {
if (gpr_t & 0x3) {
str << "r" << n << " [";
str << ((gpr_t & 1) ? "R" : " ");
str << ((gpr_t & 2) ? "W" : " ");
str << "] ";
}
gpr_t >>= 2;
}
}
if (fpr) {
uint64_t fpr_t = fpr;
for (size_t n = 0; n < 32; n++) {
if (fpr_t & 0x3) {
str << "f" << n << " [";
str << ((fpr_t & 1) ? "R" : " ");
str << ((fpr_t & 2) ? "W" : " ");
str << "] ";
}
fpr_t >>= 2;
}
}
out_str = str.str();
}
void InstrDisasm::Init(std::string name, std::string info, uint32_t flags) {
operands.clear();
special_registers.clear();
access_bits.Clear();
if (flags & InstrDisasm::kOE) {
name += "o";
InstrRegister i = {
InstrRegister::kXER, 0, InstrRegister::kReadWrite
};
special_registers.push_back(i);
}
if (flags & InstrDisasm::kRc) {
name += ".";
InstrRegister i = {
InstrRegister::kCR, 0, InstrRegister::kWrite
};
special_registers.push_back(i);
}
if (flags & InstrDisasm::kCA) {
InstrRegister i = {
InstrRegister::kXER, 0, InstrRegister::kReadWrite
};
special_registers.push_back(i);
}
if (flags & InstrDisasm::kLR) {
name += "l";
InstrRegister i = {
InstrRegister::kLR, 0, InstrRegister::kWrite
};
special_registers.push_back(i);
}
XEIGNORE(xestrcpya(this->name, XECOUNT(this->name), name.c_str()));
XEIGNORE(xestrcpya(this->info, XECOUNT(this->info), info.c_str()));
}
void InstrDisasm::AddLR(InstrRegister::Access access) {
InstrRegister i = {
InstrRegister::kLR, 0, access
};
special_registers.push_back(i);
}
void InstrDisasm::AddCTR(InstrRegister::Access access) {
InstrRegister i = {
InstrRegister::kCTR, 0, access
};
special_registers.push_back(i);
}
void InstrDisasm::AddCR(uint32_t bf, InstrRegister::Access access) {
InstrRegister i = {
InstrRegister::kCR, bf, access
};
special_registers.push_back(i);
}
void InstrDisasm::AddRegOperand(
InstrRegister::RegisterSet set, uint32_t ordinal,
InstrRegister::Access access, std::string display) {
InstrRegister i = {
set, ordinal, access
};
InstrOperand o;
o.type = InstrOperand::kRegister;
o.reg = i;
if (!display.size()) {
std::stringstream display_out;
switch (set) {
case InstrRegister::kXER:
display_out << "XER";
break;
case InstrRegister::kLR:
display_out << "LR";
break;
case InstrRegister::kCTR:
display_out << "CTR";
break;
case InstrRegister::kCR:
display_out << "CR";
display_out << ordinal;
break;
case InstrRegister::kFPSCR:
display_out << "FPSCR";
break;
case InstrRegister::kGPR:
display_out << "r";
display_out << ordinal;
break;
case InstrRegister::kFPR:
display_out << "f";
display_out << ordinal;
break;
case InstrRegister::kVMX:
display_out << "v";
display_out << ordinal;
break;
}
display = display_out.str();
}
XEIGNORE(xestrcpya(o.display, XECOUNT(o.display), display.c_str()));
operands.push_back(o);
}
void InstrDisasm::AddSImmOperand(uint64_t value, size_t width,
std::string display) {
InstrOperand o;
o.type = InstrOperand::kImmediate;
o.imm.is_signed = true;
o.imm.value = value;
o.imm.width = value;
if (display.size()) {
XEIGNORE(xestrcpya(o.display, XECOUNT(o.display), display.c_str()));
} else {
const size_t max_count = XECOUNT(o.display);
switch (width) {
case 1:
xesnprintfa(o.display, max_count, "%d", (int32_t)(int8_t)value);
break;
case 2:
xesnprintfa(o.display, max_count, "%d", (int32_t)(int16_t)value);
break;
case 4:
xesnprintfa(o.display, max_count, "%d", (int32_t)value);
break;
case 8:
xesnprintfa(o.display, max_count, "%lld", (int64_t)value);
break;
}
}
operands.push_back(o);
}
void InstrDisasm::AddUImmOperand(uint64_t value, size_t width,
std::string display) {
InstrOperand o;
o.type = InstrOperand::kImmediate;
o.imm.is_signed = false;
o.imm.value = value;
o.imm.width = value;
if (display.size()) {
XEIGNORE(xestrcpya(o.display, XECOUNT(o.display), display.c_str()));
} else {
const size_t max_count = XECOUNT(o.display);
switch (width) {
case 1:
xesnprintfa(o.display, max_count, "0x%.2X", (uint8_t)value);
break;
case 2:
xesnprintfa(o.display, max_count, "0x%.4X", (uint16_t)value);
break;
case 4:
xesnprintfa(o.display, max_count, "0x%.8X", (uint32_t)value);
break;
case 8:
xesnprintfa(o.display, max_count, "0x%.16llX", value);
break;
}
}
operands.push_back(o);
}
int InstrDisasm::Finish() {
for (std::vector<InstrOperand>::iterator it = operands.begin();
it != operands.end(); ++it) {
if (it->type == InstrOperand::kRegister) {
access_bits.MarkAccess(it->reg);
}
}
for (std::vector<InstrRegister>::iterator it = special_registers.begin();
it != special_registers.end(); ++it) {
access_bits.MarkAccess(*it);
}
return 0;
}
void InstrDisasm::Dump(std::string& str, size_t pad) {
str = name;
if (operands.size()) {
if (pad && str.size() < pad) {
str += std::string(pad - str.size(), ' ');
}
for (std::vector<InstrOperand>::iterator it = operands.begin();
it != operands.end(); ++it) {
str += it->display;
if (it + 1 != operands.end()) {
str += ", ";
}
}
}
}
InstrType* xe::cpu::ppc::GetInstrType(uint32_t code) {
InstrType* slot = NULL;
switch (code >> 26) {
case 4:
// Opcode = 4, index = bits 5-0 (6)
slot = &xe::cpu::ppc::tables::instr_table_4[XESELECTBITS(code, 0, 5)];
break;
case 19:
// Opcode = 19, index = bits 10-1 (10)
slot = &xe::cpu::ppc::tables::instr_table_19[XESELECTBITS(code, 1, 10)];
break;
case 30:
// Opcode = 30, index = bits 4-1 (4)
slot = &xe::cpu::ppc::tables::instr_table_30[XESELECTBITS(code, 1, 4)];
break;
case 31:
// Opcode = 31, index = bits 10-1 (10)
slot = &xe::cpu::ppc::tables::instr_table_31[XESELECTBITS(code, 1, 10)];
break;
case 58:
// Opcode = 58, index = bits 1-0 (2)
slot = &xe::cpu::ppc::tables::instr_table_58[XESELECTBITS(code, 0, 1)];
break;
case 59:
// Opcode = 59, index = bits 5-1 (5)
slot = &xe::cpu::ppc::tables::instr_table_59[XESELECTBITS(code, 1, 5)];
break;
case 62:
// Opcode = 62, index = bits 1-0 (2)
slot = &xe::cpu::ppc::tables::instr_table_62[XESELECTBITS(code, 0, 1)];
break;
case 63:
// Opcode = 63, index = bits 10-1 (10)
slot = &xe::cpu::ppc::tables::instr_table_63[XESELECTBITS(code, 1, 10)];
break;
default:
slot = &xe::cpu::ppc::tables::instr_table[XESELECTBITS(code, 26, 31)];
break;
}
if (!slot || !slot->opcode) {
return NULL;
}
return slot;
}
int xe::cpu::ppc::RegisterInstrDisassemble(
uint32_t code, InstrDisassembleFn disassemble) {
InstrType* instr_type = GetInstrType(code);
XEASSERTNOTNULL(instr_type);
if (!instr_type) {
return 1;
}
XEASSERTNULL(instr_type->disassemble);
instr_type->disassemble = disassemble;
return 0;
}
int xe::cpu::ppc::RegisterInstrEmit(uint32_t code, InstrEmitFn emit) {
InstrType* instr_type = GetInstrType(code);
XEASSERTNOTNULL(instr_type);
if (!instr_type) {
return 1;
}
XEASSERTNULL(instr_type->emit);
instr_type->emit = emit;
return 0;
}

328
src/xenia/cpu/ppc/instr.h Normal file
View File

@@ -0,0 +1,328 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_PPC_INSTR_H_
#define XENIA_CPU_PPC_INSTR_H_
#include <xenia/common.h>
#include <string>
#include <vector>
namespace xe {
namespace cpu {
namespace ppc {
// TODO(benvanik): rename these
typedef enum {
kXEPPCInstrFormatI = 0,
kXEPPCInstrFormatB = 1,
kXEPPCInstrFormatSC = 2,
kXEPPCInstrFormatD = 3,
kXEPPCInstrFormatDS = 4,
kXEPPCInstrFormatX = 5,
kXEPPCInstrFormatXL = 6,
kXEPPCInstrFormatXFX = 7,
kXEPPCInstrFormatXFL = 8,
kXEPPCInstrFormatXS = 9,
kXEPPCInstrFormatXO = 10,
kXEPPCInstrFormatA = 11,
kXEPPCInstrFormatM = 12,
kXEPPCInstrFormatMD = 13,
kXEPPCInstrFormatMDS = 14,
kXEPPCInstrFormatVA = 15,
kXEPPCInstrFormatVX = 16,
kXEPPCInstrFormatVXR = 17,
} xe_ppc_instr_format_e;
typedef enum {
kXEPPCInstrTypeGeneral = (1 << 0),
kXEPPCInstrTypeBranch = (1 << 1),
kXEPPCInstrTypeBranchCond = kXEPPCInstrTypeBranch | (1 << 2),
kXEPPCInstrTypeBranchAlways = kXEPPCInstrTypeBranch | (1 << 3),
kXEPPCInstrTypeSyscall = (1 << 4),
} xe_ppc_instr_type_e;
typedef enum {
kXEPPCInstrFlagReserved = 0,
} xe_ppc_instr_flag_e;
class InstrType;
static inline int32_t XEEXTS16(uint32_t v) {
return (int32_t)((int16_t)v);
}
static inline int32_t XEEXTS26(uint32_t v) {
return v & 0x02000000 ? (int32_t)v | 0xFC000000 : (int32_t)(v);
}
static inline uint64_t XEMASK(uint32_t mstart, uint32_t mstop) {
// if mstart ≤ mstop then
// mask[mstart:mstop] = ones
// mask[all other bits] = zeros
// else
// mask[mstart:63] = ones
// mask[0:mstop] = ones
// mask[all other bits] = zeros
uint64_t value =
(UINT64_MAX >> mstart) ^ ((mstop >= 63) ? 0 : UINT64_MAX >> (mstop + 1));
return mstart <= mstop ? value : ~value;
}
typedef struct {
InstrType* type;
uint32_t address;
union {
uint32_t code;
// kXEPPCInstrFormatI
struct {
uint32_t LK : 1;
uint32_t AA : 1;
uint32_t LI : 24;
uint32_t : 6;
} I;
// kXEPPCInstrFormatB
struct {
uint32_t LK : 1;
uint32_t AA : 1;
uint32_t BD : 14;
uint32_t BI : 5;
uint32_t BO : 5;
uint32_t : 6;
} B;
// kXEPPCInstrFormatSC
// kXEPPCInstrFormatD
struct {
uint32_t DS : 16;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} D;
// kXEPPCInstrFormatDS
struct {
uint32_t : 2;
uint32_t DS : 14;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} DS;
// kXEPPCInstrFormatX
struct {
uint32_t Rc : 1;
uint32_t : 10;
uint32_t RB : 5;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} X;
// kXEPPCInstrFormatXL
struct {
uint32_t LK : 1;
uint32_t : 10;
uint32_t BB : 5;
uint32_t BI : 5;
uint32_t BO : 5;
uint32_t : 6;
} XL;
// kXEPPCInstrFormatXFX
struct {
uint32_t : 1;
uint32_t : 10;
uint32_t spr : 10;
uint32_t RT : 5;
uint32_t : 6;
} XFX;
// kXEPPCInstrFormatXFL
// kXEPPCInstrFormatXS
struct {
uint32_t Rc : 1;
uint32_t SH5 : 1;
uint32_t : 9;
uint32_t SH : 5;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} XS;
// kXEPPCInstrFormatXO
struct {
uint32_t Rc : 1;
uint32_t : 9;
uint32_t OE : 1;
uint32_t RB : 5;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} XO;
// kXEPPCInstrFormatA
// kXEPPCInstrFormatM
struct {
uint32_t Rc : 1;
uint32_t ME : 5;
uint32_t MB : 5;
uint32_t SH : 5;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} M;
// kXEPPCInstrFormatMD
struct {
uint32_t Rc : 1;
uint32_t SH5 : 1;
uint32_t : 3;
uint32_t MB5 : 1;
uint32_t MB : 5;
uint32_t SH : 5;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} MD;
// kXEPPCInstrFormatMDS
struct {
uint32_t Rc : 1;
uint32_t : 4;
uint32_t MB5 : 1;
uint32_t MB : 5;
uint32_t RB : 5;
uint32_t RA : 5;
uint32_t RT : 5;
uint32_t : 6;
} MDS;
// kXEPPCInstrFormatVA
// kXEPPCInstrFormatVX
// kXEPPCInstrFormatVXR
};
} InstrData;
typedef struct {
enum RegisterSet {
kXER,
kLR,
kCTR,
kCR, // 0-7
kFPSCR,
kGPR, // 0-31
kFPR, // 0-31
kVMX, // 0-127
};
enum Access {
kRead = 1 << 0,
kWrite = 1 << 1,
kReadWrite = kRead | kWrite,
};
RegisterSet set;
uint32_t ordinal;
Access access;
} InstrRegister;
typedef struct {
enum OperandType {
kRegister,
kImmediate,
};
OperandType type;
union {
InstrRegister reg;
struct {
bool is_signed;
uint64_t value;
size_t width;
} imm;
};
char display[32];
} InstrOperand;
class InstrAccessBits {
public:
InstrAccessBits() : spr(0), cr(0), gpr(0), fpr(0) {}
// Bitmasks derived from the accesses to registers.
// Format is 2 bits for each register, even bits indicating reads and odds
// indicating writes.
uint64_t spr; // fpcsr/ctr/lr/xer
uint64_t cr; // cr7/6/5/4/3/2/1/0
uint64_t gpr; // r31-0
uint64_t fpr; // f31-0
void Clear();
void Extend(InstrAccessBits& other);
void MarkAccess(InstrRegister& reg);
void Dump(std::string& out_str);
};
class InstrDisasm {
public:
enum Flags {
kOE = 1 << 0,
kRc = 1 << 1,
kCA = 1 << 2,
kLR = 1 << 4,
};
char name[16];
char info[64];
std::vector<InstrOperand> operands;
std::vector<InstrRegister> special_registers;
InstrAccessBits access_bits;
void Init(std::string name, std::string info, uint32_t flags);
void AddLR(InstrRegister::Access access);
void AddCTR(InstrRegister::Access access);
void AddCR(uint32_t bf, InstrRegister::Access access);
void AddRegOperand(InstrRegister::RegisterSet set, uint32_t ordinal,
InstrRegister::Access access, std::string display = "");
void AddSImmOperand(uint64_t value, size_t width, std::string display = "");
void AddUImmOperand(uint64_t value, size_t width, std::string display = "");
int Finish();
void Dump(std::string& str, size_t pad = 8);
};
typedef int (*InstrDisassembleFn)(InstrData& i, InstrDisasm& d);
typedef void* InstrEmitFn;
class InstrType {
public:
uint32_t opcode;
uint32_t format; // xe_ppc_instr_format_e
uint32_t type; // xe_ppc_instr_type_e
uint32_t flags; // xe_ppc_instr_flag_e
char name[16];
InstrDisassembleFn disassemble;
InstrEmitFn emit;
};
InstrType* GetInstrType(uint32_t code);
int RegisterInstrDisassemble(uint32_t code, InstrDisassembleFn disassemble);
int RegisterInstrEmit(uint32_t code, InstrEmitFn emit);
} // namespace ppc
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_PPC_INSTR_H_

View File

@@ -0,0 +1,342 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_PPC_INSTR_TABLE_H_
#define XENIA_CPU_PPC_INSTR_TABLE_H_
#include <xenia/cpu/ppc/instr.h>
namespace xe {
namespace cpu {
namespace ppc {
namespace tables {
static InstrType* instr_table_prep(
InstrType* unprep, int unprep_count, int a, int b) {
int prep_count = pow(2.0, b - a + 1);
InstrType* prep = (InstrType*)xe_calloc(prep_count * sizeof(InstrType));
for (int n = 0; n < unprep_count; n++) {
int ordinal = XESELECTBITS(unprep[n].opcode, a, b);
prep[ordinal] = unprep[n];
}
return prep;
}
#define EMPTY(slot) {0}
#define INSTRUCTION(name, opcode, format, type, flag) { \
opcode, \
kXEPPCInstrFormat##format, \
kXEPPCInstrType##type, \
flag, \
#name, \
}
#define FLAG(t) kXEPPCInstrFlag##t
// This table set is constructed from:
// pem_64bit_v3.0.2005jul15.pdf, A.2
// PowerISA_V2.06B_V2_PUBLIC.pdf
// Opcode = 4, index = bits 5-0 (6)
static InstrType instr_table_4_unprep[] = {
// TODO: all of the vector ops
INSTRUCTION(vperm, 0x1000002B, VA , General , 0),
};
static InstrType* instr_table_4 = instr_table_prep(
instr_table_4_unprep, XECOUNT(instr_table_4_unprep), 0, 5);
// Opcode = 19, index = bits 10-1 (10)
static InstrType instr_table_19_unprep[] = {
INSTRUCTION(mcrf, 0x4C000000, XL , General , 0),
INSTRUCTION(bclrx, 0x4C000020, XL , BranchCond , 0),
INSTRUCTION(crnor, 0x4C000042, XL , General , 0),
INSTRUCTION(crandc, 0x4C000102, XL , General , 0),
INSTRUCTION(isync, 0x4C00012C, XL , General , 0),
INSTRUCTION(crxor, 0x4C000182, XL , General , 0),
INSTRUCTION(crnand, 0x4C0001C2, XL , General , 0),
INSTRUCTION(crand, 0x4C000202, XL , General , 0),
INSTRUCTION(creqv, 0x4C000242, XL , General , 0),
INSTRUCTION(crorc, 0x4C000342, XL , General , 0),
INSTRUCTION(cror, 0x4C000382, XL , General , 0),
INSTRUCTION(bcctrx, 0x4C000420, XL , BranchCond , 0),
};
static InstrType* instr_table_19 = instr_table_prep(
instr_table_19_unprep, XECOUNT(instr_table_19_unprep), 1, 10);
// Opcode = 30, index = bits 4-1 (4)
static InstrType instr_table_30_unprep[] = {
INSTRUCTION(rldiclx, 0x78000000, MD , General , 0),
INSTRUCTION(rldicrx, 0x78000004, MD , General , 0),
INSTRUCTION(rldicx, 0x78000008, MD , General , 0),
INSTRUCTION(rldimix, 0x7800000C, MD , General , 0),
INSTRUCTION(rldclx, 0x78000010, MDS, General , 0),
INSTRUCTION(rldcrx, 0x78000012, MDS, General , 0),
};
static InstrType* instr_table_30 = instr_table_prep(
instr_table_30_unprep, XECOUNT(instr_table_30_unprep), 1, 4);
// Opcode = 31, index = bits 10-1 (10)
static InstrType instr_table_31_unprep[] = {
INSTRUCTION(cmp, 0x7C000000, X , General , 0),
INSTRUCTION(tw, 0x7C000008, X , General , 0),
INSTRUCTION(lvsl, 0x7C00000C, X , General , 0),
INSTRUCTION(lvebx, 0x7C00000E, X , General , 0),
INSTRUCTION(subfcx, 0x7C000010, XO , General , 0),
INSTRUCTION(mulhdux, 0x7C000012, XO , General , 0),
INSTRUCTION(addcx, 0X7C000014, XO , General , 0),
INSTRUCTION(mulhwux, 0x7C000016, XO , General , 0),
INSTRUCTION(mfcr, 0x7C000026, X , General , 0),
INSTRUCTION(lwarx, 0x7C000028, X , General , 0),
INSTRUCTION(ldx, 0x7C00002A, X , General , 0),
INSTRUCTION(lwzx, 0x7C00002E, X , General , 0),
INSTRUCTION(slwx, 0x7C000030, X , General , 0),
INSTRUCTION(cntlzwx, 0x7C000034, X , General , 0),
INSTRUCTION(sldx, 0x7C000036, X , General , 0),
INSTRUCTION(andx, 0x7C000038, X , General , 0),
INSTRUCTION(cmpl, 0x7C000040, X , General , 0),
INSTRUCTION(lvsr, 0x7C00004C, X , General , 0),
INSTRUCTION(lvehx, 0x7C00004E, X , General , 0),
INSTRUCTION(subfx, 0x7C000050, XO , General , 0),
INSTRUCTION(ldux, 0x7C00006A, X , General , 0),
INSTRUCTION(dcbst, 0x7C00006C, X , General , 0),
INSTRUCTION(lwzux, 0x7C00006E, X , General , 0),
INSTRUCTION(cntlzdx, 0x7C000074, X , General , 0),
INSTRUCTION(andcx, 0x7C000078, X , General , 0),
INSTRUCTION(td, 0x7C000088, X , General , 0),
INSTRUCTION(lvewx, 0x7C00008E, X , General , 0),
INSTRUCTION(mulhdx, 0x7C000092, XO , General , 0),
INSTRUCTION(mulhwx, 0x7C000096, XO , General , 0),
INSTRUCTION(ldarx, 0x7C0000A8, X , General , 0),
INSTRUCTION(dcbf, 0x7C0000AC, X , General , 0),
INSTRUCTION(lbzx, 0x7C0000AE, X , General , 0),
INSTRUCTION(lvx, 0x7C0000CE, X , General , 0),
INSTRUCTION(negx, 0x7C0000D0, XO , General , 0),
INSTRUCTION(lbzux, 0x7C0000EE, X , General , 0),
INSTRUCTION(norx, 0x7C0000F8, X , General , 0),
INSTRUCTION(stvebx, 0x7C00010E, X , General , 0),
INSTRUCTION(subfex, 0x7C000110, XO , General , 0),
INSTRUCTION(addex, 0x7C000114, XO , General , 0),
INSTRUCTION(mtcrf, 0x7C000120, XFX, General , 0),
INSTRUCTION(stdx, 0x7C00012A, X , General , 0),
INSTRUCTION(stwcx, 0x7C00012D, X , General , 0),
INSTRUCTION(stwx, 0x7C00012E, X , General , 0),
INSTRUCTION(stvehx, 0x7C00014E, X , General , 0),
INSTRUCTION(stdux, 0x7C00016A, X , General , 0),
INSTRUCTION(stwux, 0x7C00016E, X , General , 0),
INSTRUCTION(stvewx, 0x7C00018E, X , General , 0),
INSTRUCTION(subfzex, 0x7C000190, XO , General , 0),
INSTRUCTION(addzex, 0x7C000194, XO , General , 0),
INSTRUCTION(stdcx, 0x7C0001AD, X , General , 0),
INSTRUCTION(stbx, 0x7C0001AE, X , General , 0),
INSTRUCTION(stvx, 0x7C0001CE, X , General , 0),
INSTRUCTION(subfmex, 0x7C0001D0, XO , General , 0),
INSTRUCTION(mulldx, 0x7C0001D2, XO , General , 0),
INSTRUCTION(addmex, 0x7C0001D4, XO , General , 0),
INSTRUCTION(mullwx, 0x7C0001D6, XO , General , 0),
INSTRUCTION(dcbtst, 0x7C0001EC, X , General , 0),
INSTRUCTION(stbux, 0x7C0001EE, X , General , 0),
INSTRUCTION(addx, 0x7C000214, XO , General , 0),
INSTRUCTION(dcbt, 0x7C00022C, X , General , 0),
INSTRUCTION(lhzx, 0x7C00022E, X , General , 0),
INSTRUCTION(eqvx, 0x7C000238, X , General , 0),
INSTRUCTION(eciwx, 0x7C00026C, X , General , 0),
INSTRUCTION(lhzux, 0x7C00026E, X , General , 0),
INSTRUCTION(xorx, 0x7C000278, X , General , 0),
INSTRUCTION(mfspr, 0x7C0002A6, XFX, General , 0),
INSTRUCTION(lwax, 0x7C0002AA, X , General , 0),
INSTRUCTION(lhax, 0x7C0002AE, X , General , 0),
INSTRUCTION(lvxl, 0x7C0002CE, X , General , 0),
INSTRUCTION(mftb, 0x7C0002E6, XFX, General , 0),
INSTRUCTION(lwaux, 0x7C0002EA, X , General , 0),
INSTRUCTION(lhaux, 0x7C0002EE, X , General , 0),
INSTRUCTION(sthx, 0x7C00032E, X , General , 0),
INSTRUCTION(orcx, 0x7C000338, X , General , 0),
INSTRUCTION(ecowx, 0x7C00036C, X , General , 0),
INSTRUCTION(sthux, 0x7C00036E, X , General , 0),
INSTRUCTION(orx, 0x7C000378, X , General , 0),
INSTRUCTION(divdux, 0x7C000392, XO , General , 0),
INSTRUCTION(divwux, 0x7C000396, XO , General , 0),
INSTRUCTION(mtspr, 0x7C0003A6, XFX, General , 0),
INSTRUCTION(nandx, 0x7C0003B8, X , General , 0),
INSTRUCTION(stvxl, 0x7C0003CE, X , General , 0),
INSTRUCTION(divdx, 0x7C0003D2, XO , General , 0),
INSTRUCTION(divwx, 0x7C0003D6, XO , General , 0),
INSTRUCTION(lvlx, 0x7C00040E, X , General , 0),
INSTRUCTION(ldbrx, 0x7C000428, X , General , 0),
INSTRUCTION(lswx, 0x7C00042A, X , General , 0),
INSTRUCTION(lwbrx, 0x7C00042C, X , General , 0),
INSTRUCTION(lfsx, 0x7C00042E, X , General , 0),
INSTRUCTION(srwx, 0x7C000430, X , General , 0),
INSTRUCTION(srdx, 0x7C000436, X , General , 0),
INSTRUCTION(lfsux, 0x7C00046E, X , General , 0),
INSTRUCTION(lswi, 0x7C0004AA, X , General , 0),
INSTRUCTION(sync, 0x7C0004AC, X , General , 0),
INSTRUCTION(lfdx, 0x7C0004AE, X , General , 0),
INSTRUCTION(lfdux, 0x7C0004EE, X , General , 0),
INSTRUCTION(stdbrx, 0x7C000528, X , General , 0),
INSTRUCTION(stswx, 0x7C00052A, X , General , 0),
INSTRUCTION(stwbrx, 0x7C00052C, X , General , 0),
INSTRUCTION(stfsx, 0x7C00052E, X , General , 0),
INSTRUCTION(stfsux, 0x7C00056E, X , General , 0),
INSTRUCTION(stswi, 0x7C0005AA, X , General , 0),
INSTRUCTION(stfdx, 0x7C0005AE, X , General , 0),
INSTRUCTION(stfdux, 0x7C0005EE, X , General , 0),
INSTRUCTION(lhbrx, 0x7C00062C, X , General , 0),
INSTRUCTION(srawx, 0x7C000630, X , General , 0),
INSTRUCTION(sradx, 0x7C000634, X , General , 0),
INSTRUCTION(srawix, 0x7C000670, X , General , 0),
INSTRUCTION(sradix, 0x7C000674, XS , General , 0), // TODO
INSTRUCTION(eieio, 0x7C0006AC, X , General , 0),
INSTRUCTION(sthbrx, 0x7C00072C, X , General , 0),
INSTRUCTION(extshx, 0x7C000734, X , General , 0),
INSTRUCTION(extsbx, 0x7C000774, X , General , 0),
INSTRUCTION(icbi, 0x7C0007AC, X , General , 0),
INSTRUCTION(stfiwx, 0x7C0007AE, X , General , 0),
INSTRUCTION(extswx, 0x7C0007B4, X , General , 0),
INSTRUCTION(dcbz, 0x7C0007EC, X , General , 0), // 0x7C2007EC = DCBZ128
};
static InstrType* instr_table_31 = instr_table_prep(
instr_table_31_unprep, XECOUNT(instr_table_31_unprep), 1, 10);
// Opcode = 58, index = bits 1-0 (2)
static InstrType instr_table_58_unprep[] = {
INSTRUCTION(ld, 0xE8000000, DS , General , 0),
INSTRUCTION(ldu, 0xE8000001, DS , General , 0),
INSTRUCTION(lwa, 0xE8000002, DS , General , 0),
};
static InstrType* instr_table_58 = instr_table_prep(
instr_table_58_unprep, XECOUNT(instr_table_58_unprep), 0, 1);
// Opcode = 59, index = bits 5-1 (5)
static InstrType instr_table_59_unprep[] = {
INSTRUCTION(fdivsx, 0xEC000024, A , General , 0),
INSTRUCTION(fsubsx, 0xEC000028, A , General , 0),
INSTRUCTION(faddsx, 0xEC00002A, A , General , 0),
INSTRUCTION(fsqrtsx, 0xEC00002C, A , General , 0),
INSTRUCTION(fresx, 0xEC000030, A , General , 0),
INSTRUCTION(fmulsx, 0xEC000032, A , General , 0),
INSTRUCTION(fmsubsx, 0xEC000038, A , General , 0),
INSTRUCTION(fmaddsx, 0xEC00003A, A , General , 0),
INSTRUCTION(fnmsubsx, 0xEC00003C, A , General , 0),
INSTRUCTION(fnmaddsx, 0xEC00003E, A , General , 0),
};
static InstrType* instr_table_59 = instr_table_prep(
instr_table_59_unprep, XECOUNT(instr_table_59_unprep), 1, 5);
// Opcode = 62, index = bits 1-0 (2)
static InstrType instr_table_62_unprep[] = {
INSTRUCTION(std, 0xF8000000, DS , General , 0),
INSTRUCTION(stdu, 0xF8000001, DS , General , 0),
};
static InstrType* instr_table_62 = instr_table_prep(
instr_table_62_unprep, XECOUNT(instr_table_62_unprep), 0, 1);
// Opcode = 63, index = bits 10-1 (10)
static InstrType instr_table_63_unprep[] = {
INSTRUCTION(fcmpu, 0xFC000000, X , General , 0),
INSTRUCTION(frspx, 0xFC000018, X , General , 0),
INSTRUCTION(fctiwx, 0xFC00001C, X , General , 0),
INSTRUCTION(fctiwzx, 0xFC00001E, X , General , 0),
INSTRUCTION(fdivx, 0xFC000024, A , General , 0),
INSTRUCTION(fsubx, 0xFC000028, A , General , 0),
INSTRUCTION(faddx, 0xFC00002A, A , General , 0),
INSTRUCTION(fsqrtx, 0xFC00002C, A , General , 0),
INSTRUCTION(fselx, 0xFC00002E, A , General , 0),
INSTRUCTION(fmulx, 0xFC000032, A , General , 0),
INSTRUCTION(frsqrtex, 0xFC000034, A , General , 0),
INSTRUCTION(fmsubx, 0xFC000038, A , General , 0),
INSTRUCTION(fmaddx, 0xFC00003A, A , General , 0),
INSTRUCTION(fnmsubx, 0xFC00003C, A , General , 0),
INSTRUCTION(fnmaddx, 0xFC00003E, A , General , 0),
INSTRUCTION(fcmpo, 0xFC000040, X , General , 0),
INSTRUCTION(mtfsb1x, 0xFC00004C, X , General , 0),
INSTRUCTION(fnegx, 0xFC000050, X , General , 0),
INSTRUCTION(mcrfs, 0xFC000080, X , General , 0),
INSTRUCTION(mtfsb0x, 0xFC00008C, X , General , 0),
INSTRUCTION(fmrx, 0xFC000090, X , General , 0),
INSTRUCTION(mtfsfix, 0xFC00010C, X , General , 0),
INSTRUCTION(fnabsx, 0xFC000110, X , General , 0),
INSTRUCTION(fabsx, 0xFC000210, X , General , 0),
INSTRUCTION(mffsx, 0xFC00048E, X , General , 0),
INSTRUCTION(mtfsfx, 0xFC00058E, XFL, General , 0),
INSTRUCTION(fctidx, 0xFC00065C, X , General , 0),
INSTRUCTION(fctidzx, 0xFC00065E, X , General , 0),
INSTRUCTION(fcfidx, 0xFC00069C, X , General , 0),
};
static InstrType* instr_table_63 = instr_table_prep(
instr_table_63_unprep, XECOUNT(instr_table_63_unprep), 1, 10);
// Main table, index = bits 31-26 (6) : (code >> 26)
static InstrType instr_table_unprep[64] = {
INSTRUCTION(tdi, 0x08000000, D , General , 0),
INSTRUCTION(twi, 0x0C000000, D , General , 0),
INSTRUCTION(mulli, 0x1C000000, D , General , 0),
INSTRUCTION(subficx, 0x20000000, D , General , 0),
INSTRUCTION(cmpli, 0x28000000, D , General , 0),
INSTRUCTION(cmpi, 0x2C000000, D , General , 0),
INSTRUCTION(addic, 0x30000000, D , General , 0),
INSTRUCTION(addicx, 0x34000000, D , General , 0),
INSTRUCTION(addi, 0x38000000, D , General , 0),
INSTRUCTION(addis, 0x3C000000, D , General , 0),
INSTRUCTION(bcx, 0x40000000, B , BranchCond , 0),
INSTRUCTION(sc, 0x44000002, SC , Syscall , 0),
INSTRUCTION(bx, 0x48000000, I , BranchAlways , 0),
INSTRUCTION(rlwimix, 0x50000000, M , General , 0),
INSTRUCTION(rlwinmx, 0x54000000, M , General , 0),
INSTRUCTION(rlwnmx, 0x5C000000, M , General , 0),
INSTRUCTION(ori, 0x60000000, D , General , 0),
INSTRUCTION(oris, 0x64000000, D , General , 0),
INSTRUCTION(xori, 0x68000000, D , General , 0),
INSTRUCTION(xoris, 0x6C000000, D , General , 0),
INSTRUCTION(andix, 0x70000000, D , General , 0),
INSTRUCTION(andisx, 0x74000000, D , General , 0),
INSTRUCTION(lwz, 0x80000000, D , General , 0),
INSTRUCTION(lwzu, 0x84000000, D , General , 0),
INSTRUCTION(lbz, 0x88000000, D , General , 0),
INSTRUCTION(lbzu, 0x8C000000, D , General , 0),
INSTRUCTION(stw, 0x90000000, D , General , 0),
INSTRUCTION(stwu, 0x94000000, D , General , 0),
INSTRUCTION(stb, 0x98000000, D , General , 0),
INSTRUCTION(stbu, 0x9C000000, D , General , 0),
INSTRUCTION(lhz, 0xA0000000, D , General , 0),
INSTRUCTION(lhzu, 0xA4000000, D , General , 0),
INSTRUCTION(lha, 0xA8000000, D , General , 0),
INSTRUCTION(lhau, 0xAC000000, D , General , 0),
INSTRUCTION(sth, 0xB0000000, D , General , 0),
INSTRUCTION(sthu, 0xB4000000, D , General , 0),
INSTRUCTION(lmw, 0xB8000000, D , General , 0),
INSTRUCTION(stmw, 0xBC000000, D , General , 0),
INSTRUCTION(lfs, 0xC0000000, D , General , 0),
INSTRUCTION(lfsu, 0xC4000000, D , General , 0),
INSTRUCTION(lfd, 0xC8000000, D , General , 0),
INSTRUCTION(lfdu, 0xCC000000, D , General , 0),
INSTRUCTION(stfs, 0xD0000000, D , General , 0),
INSTRUCTION(stfsu, 0xD4000000, D , General , 0),
INSTRUCTION(stfd, 0xD8000000, D , General , 0),
INSTRUCTION(stfdu, 0xDC000000, D , General , 0),
};
static InstrType* instr_table = instr_table_prep(
instr_table_unprep, XECOUNT(instr_table_unprep), 26, 31);
#undef FLAG
#undef INSTRUCTION
#undef EMPTY
} // namespace tables
} // namespace ppc
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_PPC_INSTR_TABLE_H_

View File

@@ -0,0 +1,10 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'instr.cc',
'instr.h',
'instr_tables.h',
'state.cc',
'state.h',
],
}

View File

@@ -0,0 +1,47 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/cpu/ppc/state.h>
namespace {
uint64_t ParseInt64(const char* value) {
return xestrtoulla(value, NULL, 0);
}
}
void xe_ppc_state::SetRegFromString(const char* name, const char* value) {
int n;
if (sscanf(name, "r%d", &n) == 1) {
this->r[n] = ParseInt64(value);
} else {
printf("Unrecognized register name: %s\n", name);
}
}
bool xe_ppc_state::CompareRegWithString(
const char* name, const char* value,
char* out_value, size_t out_value_size) {
int n;
if (sscanf(name, "r%d", &n) == 1) {
uint64_t expected = ParseInt64(value);
if (this->r[n] != expected) {
xesnprintfa(out_value, out_value_size, "%016llX", this->r[n]);
return false;
}
return true;
} else {
printf("Unrecognized register name: %s\n", name);
return false;
}
}

169
src/xenia/cpu/ppc/state.h Normal file
View File

@@ -0,0 +1,169 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_PPC_STATE_H_
#define XENIA_CPU_PPC_STATE_H_
/**
* NOTE: this file is included by xethunk and as such should have a *MINIMAL*
* set of dependencies!
*/
#include <stdint.h>
#ifdef XE_THUNK
#define XECACHEALIGN __attribute__ ((aligned(8)))
#define XECACHEALIGN64 __attribute__ ((aligned(64)))
#endif
// namespace FPRF {
// enum FPRF_e {
// QUIET_NAN = 0x00088000,
// NEG_INFINITY = 0x00090000,
// NEG_NORMALIZED = 0x00010000,
// NEG_DENORMALIZED = 0x00018000,
// NEG_ZERO = 0x00048000,
// POS_ZERO = 0x00040000,
// POS_DENORMALIZED = 0x00028000,
// POS_NORMALIZED = 0x00020000,
// POS_INFINITY = 0x000A0000,
// };
// } // FPRF
#define kXEPPCRegLR 0xFFFF0001
#define kXEPPCRegCTR 0xFFFF0002
typedef struct XECACHEALIGN xe_float4 {
union {
struct {
float x;
float y;
float z;
float w;
};
float f4[4];
struct {
uint64_t low;
uint64_t high;
};
};
} xe_float4_t;
typedef struct XECACHEALIGN64 xe_ppc_state {
uint32_t cia; // Current PC (CIA)
uint32_t nia; // Next PC (NIA)
uint64_t xer; // XER register
uint64_t lr; // Link register
uint64_t ctr; // Count register
uint64_t r[32]; // General purpose registers
xe_float4_t v[128]; // VMX128 vector registers
double f[32]; // Floating-point registers
union {
uint32_t value;
struct {
uint8_t lt :1; // Negative (LT) - result is negative
uint8_t gt :1; // Positive (GT) - result is positive (and not zero)
uint8_t eq :1; // Zero (EQ) - result is zero or a stwcx/stdcx completed successfully
uint8_t so :1; // Summary Overflow (SO) - copy of XER[SO]
} cr0;
struct {
uint8_t fx :1; // FP exception summary - copy of FPSCR[FX]
uint8_t fex :1; // FP enabled exception summary - copy of FPSCR[FEX]
uint8_t vx :1; // FP invalid operation exception summary - copy of FPSCR[VX]
uint8_t ox :1; // FP overflow exception - copy of FPSCR[OX]
} cr1;
struct {
uint8_t value :4;
} cr2;
struct {
uint8_t value :4;
} cr3;
struct {
uint8_t value :4;
} cr4;
struct {
uint8_t value :4;
} cr5;
struct {
uint8_t value :4;
} cr6;
struct {
uint8_t value :4;
} cr7;
} cr; // Condition register
union {
uint32_t value;
struct {
uint8_t fx :1; // FP exception summary -- sticky
uint8_t fex :1; // FP enabled exception summary
uint8_t vx :1; // FP invalid operation exception summary
uint8_t ox :1; // FP overflow exception -- sticky
uint8_t ux :1; // FP underflow exception -- sticky
uint8_t zx :1; // FP zero divide exception -- sticky
uint8_t xx :1; // FP inexact exception -- sticky
uint8_t vxsnan :1; // FP invalid op exception: SNaN -- sticky
uint8_t vxisi :1; // FP invalid op exception: infinity - infinity -- sticky
uint8_t vxidi :1; // FP invalid op exception: infinity / infinity -- sticky
uint8_t vxzdz :1; // FP invalid op exception: 0 / 0 -- sticky
uint8_t vximz :1; // FP invalid op exception: infinity * 0 -- sticky
uint8_t vxvc :1; // FP invalid op exception: invalid compare -- sticky
uint8_t fr :1; // FP fraction rounded
uint8_t fi :1; // FP fraction inexact
uint8_t fprf_c :1; // FP result class
uint8_t fprf_lt :1; // FP result less than or negative (FL or <)
uint8_t fprf_gt :1; // FP result greater than or positive (FG or >)
uint8_t fprf_eq :1; // FP result equal or zero (FE or =)
uint8_t fprf_un :1; // FP result unordered or NaN (FU or ?)
uint8_t reserved :1;
uint8_t vxsoft :1; // FP invalid op exception: software request -- sticky
uint8_t vxsqrt :1; // FP invalid op exception: invalid sqrt -- sticky
uint8_t vxcvi :1; // FP invalid op exception: invalid integer convert -- sticky
uint8_t ve :1; // FP invalid op exception enable
uint8_t oe :1; // IEEE floating-point overflow exception enable
uint8_t ue :1; // IEEE floating-point underflow exception enable
uint8_t ze :1; // IEEE floating-point zero divide exception enable
uint8_t xe :1; // IEEE floating-point inexact exception enable
uint8_t ni :1; // Floating-point non-IEEE mode
uint8_t rn :2; // FP rounding control: 00 = nearest
// 01 = toward zero
// 10 = toward +infinity
// 11 = toward -infinity
} bits;
} fpscr; // Floating-point status and control register
// uint32_t get_fprf() {
// return fpscr.value & 0x000F8000;
// }
// void set_fprf(const uint32_t v) {
// fpscr.value = (fpscr.value & ~0x000F8000) | v;
// }
// Runtime-specific data pointer. Used on callbacks to get access to the
// current runtime and its data.
uint8_t* membase;
void* processor;
void* thread_state;
void* runtime;
void SetRegFromString(const char* name, const char* value);
bool CompareRegWithString(const char* name, const char* value,
char* out_value, size_t out_value_size);
} xe_ppc_state_t;
#endif // XENIA_CPU_PPC_STATE_H_

250
src/xenia/cpu/processor.cc Normal file
View File

@@ -0,0 +1,250 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/processor.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/Interpreter.h>
#include <llvm/ExecutionEngine/JIT.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/TargetSelect.h>
#include <xenia/cpu/codegen/emit.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::kernel;
namespace {
void InitializeIfNeeded();
void CleanupOnShutdown();
void InitializeIfNeeded() {
static bool has_initialized = false;
if (has_initialized) {
return;
}
has_initialized = true;
// TODO(benvanik): only do this once
LLVMLinkInInterpreter();
LLVMLinkInJIT();
InitializeNativeTarget();
llvm_start_multithreaded();
// TODO(benvanik): only do this once
codegen::RegisterEmitCategoryALU();
codegen::RegisterEmitCategoryControl();
codegen::RegisterEmitCategoryFPU();
codegen::RegisterEmitCategoryMemory();
atexit(CleanupOnShutdown);
}
void CleanupOnShutdown() {
llvm_shutdown();
}
}
Processor::Processor(xe_pal_ref pal, xe_memory_ref memory) {
pal_ = xe_pal_retain(pal);
memory_ = xe_memory_retain(memory);
InitializeIfNeeded();
}
Processor::~Processor() {
// Cleanup all modules.
for (std::vector<ExecModule*>::iterator it = modules_.begin();
it != modules_.end(); ++it) {
delete *it;
}
engine_.reset();
xe_memory_release(memory_);
xe_pal_release(pal_);
}
xe_pal_ref Processor::pal() {
return xe_pal_retain(pal_);
}
xe_memory_ref Processor::memory() {
return xe_memory_retain(memory_);
}
int Processor::Setup() {
XEASSERTNULL(engine_);
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
Module* dummy_module = new Module("dummy", *dummy_context_.get());
std::string error_message;
EngineBuilder builder(dummy_module);
builder.setEngineKind(EngineKind::JIT);
builder.setErrorStr(&error_message);
builder.setOptLevel(CodeGenOpt::None);
//builder.setOptLevel(CodeGenOpt::Aggressive);
//builder.setTargetOptions();
builder.setAllocateGVsWithCode(false);
//builder.setUseMCJIT(true);
engine_ = shared_ptr<ExecutionEngine>(builder.create());
if (!engine_) {
return 1;
}
return 0;
}
int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
shared_ptr<ExportResolver> export_resolver) {
ExecModule* exec_module = NULL;
const xechar_t* name = xestrrchr(path, '/') + 1;
// TODO(benvanik): map file from filesystem
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
if (!mmap) {
return NULL;
}
void* addr = xe_mmap_get_addr(mmap);
size_t length = xe_mmap_get_length(mmap);
int result_code = 1;
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
xe_memory_get_length(memory_),
addr, length));
// Prepare the module.
char name_a[XE_MAX_PATH];
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
char path_a[XE_MAX_PATH];
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
exec_module = new ExecModule(
memory_, export_resolver, name_a, path_a, engine_);
if (exec_module->PrepareRawBinary(start_address, start_address + length)) {
delete exec_module;
return 1;
}
exec_module->AddFunctionsToMap(all_fns_);
modules_.push_back(exec_module);
exec_module->Dump();
result_code = 0;
XECLEANUP:
if (result_code) {
delete exec_module;
}
xe_mmap_release(mmap);
return result_code;
}
int Processor::PrepareModule(const char* name, const char* path,
xe_xex2_ref xex,
shared_ptr<ExportResolver> export_resolver) {
ExecModule* exec_module = new ExecModule(
memory_, export_resolver, name, path,
engine_);
if (exec_module->PrepareXex(xex)) {
delete exec_module;
return 1;
}
exec_module->AddFunctionsToMap(all_fns_);
modules_.push_back(exec_module);
return 0;
}
uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
// TODO(benvanik): implement callback creation.
return 0;
}
ThreadState* Processor::AllocThread(uint32_t stack_size,
uint32_t thread_state_address) {
ThreadState* thread_state = new ThreadState(
this, stack_size, thread_state_address);
return thread_state;
}
void Processor::DeallocThread(ThreadState* thread_state) {
delete thread_state;
}
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
// Find the function to execute.
Function* f = GetFunction(address);
if (!f) {
XELOGCPU(XT("Failed to find function %.8X to execute."), address);
return 1;
}
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
// This could be set to anything to give us a unique identifier to track
// re-entrancy/etc.
uint32_t lr = 0xBEBEBEBE;
// Setup registers.
ppc_state->lr = lr;
// Args:
// - i8* state
// - i64 lr
std::vector<GenericValue> args;
args.push_back(PTOGV(ppc_state));
GenericValue lr_arg;
lr_arg.IntVal = APInt(64, lr);
args.push_back(lr_arg);
GenericValue ret = engine_->runFunction(f, args);
// return (uint32_t)ret.IntVal.getSExtValue();
// Faster, somewhat.
// Messes with the stack in such a way as to cause Xcode to behave oddly.
// typedef void (*fnptr)(xe_ppc_state_t*, uint64_t);
// fnptr ptr = (fnptr)engine_->getPointerToFunction(f);
// ptr(ppc_state, lr);
return 0;
}
uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
uint64_t arg0) {
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
ppc_state->r[3] = arg0;
if (Execute(thread_state, address)) {
return 0xDEADBABE;
}
return ppc_state->r[3];
}
Function* Processor::GetFunction(uint32_t address) {
FunctionMap::iterator it = all_fns_.find(address);
if (it != all_fns_.end()) {
return it->second;
}
return NULL;
}

75
src/xenia/cpu/processor.h Normal file
View File

@@ -0,0 +1,75 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_PROCESSOR_H_
#define XENIA_CPU_PROCESSOR_H_
#include <xenia/core.h>
#include <vector>
#include <xenia/cpu/exec_module.h>
#include <xenia/cpu/thread_state.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/xex2.h>
namespace llvm {
class ExecutionEngine;
class Function;
}
namespace xe {
namespace cpu {
class Processor {
public:
Processor(xe_pal_ref pal, xe_memory_ref memory);
~Processor();
xe_pal_ref pal();
xe_memory_ref memory();
int Setup();
int LoadBinary(const xechar_t* path, uint32_t start_address,
shared_ptr<kernel::ExportResolver> export_resolver);
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
shared_ptr<kernel::ExportResolver> export_resolver);
uint32_t CreateCallback(void (*callback)(void* data), void* data);
ThreadState* AllocThread(uint32_t stack_size, uint32_t thread_state_address);
void DeallocThread(ThreadState* thread_state);
int Execute(ThreadState* thread_state, uint32_t address);
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
private:
llvm::Function* GetFunction(uint32_t address);
xe_pal_ref pal_;
xe_memory_ref memory_;
shared_ptr<llvm::ExecutionEngine> engine_;
auto_ptr<llvm::LLVMContext> dummy_context_;
std::vector<ExecModule*> modules_;
FunctionMap all_fns_;
};
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_PROCESSOR_H_

18
src/xenia/cpu/sdb.h Normal file
View File

@@ -0,0 +1,18 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_SDB_H_
#define XENIA_CPU_SDB_H_
#include <xenia/cpu/sdb/raw_symbol_database.h>
#include <xenia/cpu/sdb/symbol.h>
#include <xenia/cpu/sdb/symbol_database.h>
#include <xenia/cpu/sdb/xex_symbol_database.h>
#endif // XENIA_CPU_SDB_H_

View File

@@ -0,0 +1,41 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/sdb/raw_symbol_database.h>
#include <xenia/cpu/ppc/instr.h>
using namespace std;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
RawSymbolDatabase::RawSymbolDatabase(
xe_memory_ref memory, ExportResolver* export_resolver,
uint32_t start_address, uint32_t end_address) :
SymbolDatabase(memory, export_resolver) {
start_address_ = start_address;
end_address_ = end_address;
}
RawSymbolDatabase::~RawSymbolDatabase() {
}
uint32_t RawSymbolDatabase::GetEntryPoint() {
return start_address_;
}
bool RawSymbolDatabase::IsValueInTextRange(uint32_t value) {
return value >= start_address_ && value < end_address_;
}

View File

@@ -0,0 +1,42 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
#define XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_
#include <xenia/cpu/sdb/symbol_database.h>
namespace xe {
namespace cpu {
namespace sdb {
class RawSymbolDatabase : public SymbolDatabase {
public:
RawSymbolDatabase(xe_memory_ref memory,
kernel::ExportResolver* export_resolver,
uint32_t start_address, uint32_t end_address);
virtual ~RawSymbolDatabase();
private:
virtual uint32_t GetEntryPoint();
virtual bool IsValueInTextRange(uint32_t value);
uint32_t start_address_;
uint32_t end_address_;
};
} // namespace sdb
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_SDB_RAW_SYMBOL_DATABASE_H_

View File

@@ -0,0 +1,13 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'raw_symbol_database.cc',
'raw_symbol_database.h',
'symbol.cc',
'symbol.h',
'symbol_database.cc',
'symbol_database.h',
'xex_symbol_database.cc',
'xex_symbol_database.h',
]
}

122
src/xenia/cpu/sdb/symbol.cc Normal file
View File

@@ -0,0 +1,122 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/sdb/symbol.h>
#include <xenia/cpu/ppc/instr.h>
using namespace std;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
Symbol::Symbol(SymbolType type) :
symbol_type(type),
name_(NULL) {
}
Symbol::~Symbol() {
xe_free(name_);
}
const char* Symbol::name() {
return name_;
}
void Symbol::set_name(const char* value) {
if (name_ == value) {
return;
}
if (name_) {
xe_free(name_);
}
if (value) {
name_ = xestrdupa(value);
}
}
FunctionBlock::FunctionBlock() :
start_address(0), end_address(0),
outgoing_type(kTargetUnknown), outgoing_address(0),
outgoing_function(0) {
}
FunctionSymbol::FunctionSymbol() :
Symbol(Function),
start_address(0), end_address(0),
type(Unknown), flags(0),
kernel_export(0), ee(0) {
}
FunctionSymbol::~FunctionSymbol() {
for (std::map<uint32_t, FunctionBlock*>::iterator it = blocks.begin();
it != blocks.end(); ++it) {
delete it->second;
}
}
FunctionBlock* FunctionSymbol::GetBlock(uint32_t address) {
std::map<uint32_t, FunctionBlock*>::iterator it = blocks.find(address);
if (it != blocks.end()) {
return it->second;
}
return NULL;
}
FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
// Scan to find the block that contains the address.
for (std::map<uint32_t, FunctionBlock*>::iterator it = blocks.begin();
it != blocks.end(); ++it) {
FunctionBlock* block = it->second;
if (address == block->start_address) {
// No need for a split.
return block;
} else if (address >= block->start_address &&
address <= block->end_address + 4) {
// Inside this block.
// Since we know we are starting inside of the block we split downwards.
FunctionBlock* new_block = new FunctionBlock();
new_block->start_address = address;
new_block->end_address = block->end_address;
new_block->outgoing_type = block->outgoing_type;
new_block->outgoing_address = block->outgoing_address;
new_block->outgoing_block = block->outgoing_block;
blocks.insert(std::pair<uint32_t, FunctionBlock*>(address, new_block));
// Patch up old block.
block->end_address = address - 4;
block->outgoing_type = FunctionBlock::kTargetNone;
block->outgoing_address = 0;
block->outgoing_block = NULL;
return new_block;
}
}
return NULL;
}
VariableSymbol::VariableSymbol() :
Symbol(Variable),
address(0),
kernel_export(0) {
}
VariableSymbol::~VariableSymbol() {
}
ExceptionEntrySymbol::ExceptionEntrySymbol() :
Symbol(ExceptionEntry),
address(0), function(0) {
}

151
src/xenia/cpu/sdb/symbol.h Normal file
View File

@@ -0,0 +1,151 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_SDB_SYMBOL_H_
#define XENIA_CPU_SDB_SYMBOL_H_
#include <xenia/core.h>
#include <map>
#include <vector>
#include <xenia/kernel/export.h>
namespace xe {
namespace cpu {
namespace sdb {
class FunctionSymbol;
class VariableSymbol;
class FunctionCall {
public:
uint32_t address;
FunctionSymbol* source;
FunctionSymbol* target;
};
class VariableAccess {
public:
uint32_t address;
FunctionSymbol* source;
VariableSymbol* target;
};
class Symbol {
public:
enum SymbolType {
Function = 0,
Variable = 1,
ExceptionEntry = 2,
};
virtual ~Symbol();
SymbolType symbol_type;
const char* name();
void set_name(const char* value);
protected:
Symbol(SymbolType type);
char* name_;
};
class ExceptionEntrySymbol;
class FunctionBlock {
public:
enum TargetType {
kTargetUnknown = 0,
kTargetBlock = 1,
kTargetFunction = 2,
kTargetLR = 3,
kTargetCTR = 4,
kTargetNone = 5,
};
FunctionBlock();
uint32_t start_address;
uint32_t end_address;
std::vector<FunctionBlock*> incoming_blocks;
TargetType outgoing_type;
uint32_t outgoing_address;
union {
FunctionSymbol* outgoing_function;
FunctionBlock* outgoing_block;
};
};
class FunctionSymbol : public Symbol {
public:
enum FunctionType {
Unknown = 0,
Kernel = 1,
User = 2,
};
enum Flags {
kFlagSaveGprLr = 1 << 1,
kFlagRestGprLr = 1 << 2,
};
FunctionSymbol();
virtual ~FunctionSymbol();
FunctionBlock* GetBlock(uint32_t address);
FunctionBlock* SplitBlock(uint32_t address);
uint32_t start_address;
uint32_t end_address;
FunctionType type;
uint32_t flags;
kernel::KernelExport* kernel_export;
ExceptionEntrySymbol* ee;
std::vector<FunctionCall*> incoming_calls;
std::vector<FunctionCall*> outgoing_calls;
std::vector<VariableAccess*> variable_accesses;
std::map<uint32_t, FunctionBlock*> blocks;
};
class VariableSymbol : public Symbol {
public:
VariableSymbol();
virtual ~VariableSymbol();
uint32_t address;
kernel::KernelExport* kernel_export;
};
class ExceptionEntrySymbol : public Symbol {
public:
ExceptionEntrySymbol();
virtual ~ExceptionEntrySymbol() {}
uint32_t address;
FunctionSymbol* function;
};
} // namespace sdb
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_SDB_SYMBOL_H_

View File

@@ -0,0 +1,748 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/sdb/symbol_database.h>
#include <fstream>
#include <sstream>
#include <xenia/cpu/ppc/instr.h>
using namespace std;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
SymbolDatabase::SymbolDatabase(xe_memory_ref memory,
ExportResolver* export_resolver) {
memory_ = xe_memory_retain(memory);
export_resolver_ = export_resolver;
}
SymbolDatabase::~SymbolDatabase() {
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
delete it->second;
}
xe_memory_release(memory_);
}
int SymbolDatabase::Analyze() {
// Iteratively run passes over the db.
// This uses a queue to do a breadth-first search of all accessible
// functions. Callbacks and such likely won't be hit.
// Queue entry point of the application.
FunctionSymbol* fn = GetOrInsertFunction(GetEntryPoint());
fn->set_name("start");
// Keep pumping the queue until there's nothing left to do.
FlushQueue();
// Do a pass over the functions to fill holes. A few times. Just to be safe.
for (size_t n = 0; n < 4; n++) {
if (!FillHoles()) {
break;
}
FlushQueue();
}
// Run a pass over all functions and link up their extended data.
// This can only be performed after we have all functions and basic blocks.
bool needs_another_pass = false;
do {
needs_another_pass = false;
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end();
++it) {
if (it->second->symbol_type == Symbol::Function) {
if (fn->type == FunctionSymbol::Unknown) {
XELOGE(XT("UNKNOWN FN %.8X"), fn->start_address);
}
if (CompleteFunctionGraph(static_cast<FunctionSymbol*>(it->second))) {
needs_another_pass = true;
}
}
}
if (needs_another_pass) {
FlushQueue();
}
} while (needs_another_pass);
return 0;
}
Symbol* SymbolDatabase::GetSymbol(uint32_t address) {
SymbolMap::iterator i = symbols_.find(address);
if (i != symbols_.end()) {
return i->second;
}
return NULL;
}
ExceptionEntrySymbol* SymbolDatabase::GetOrInsertExceptionEntry(
uint32_t address) {
SymbolMap::iterator i = symbols_.find(address);
if (i != symbols_.end() && i->second->symbol_type == Symbol::Function) {
return static_cast<ExceptionEntrySymbol*>(i->second);
}
ExceptionEntrySymbol* ee = new ExceptionEntrySymbol();
ee->address = address;
symbols_.insert(SymbolMap::value_type(address, ee));
return ee;
}
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
FunctionSymbol* fn = GetFunction(address);
if (fn) {
return fn;
}
// Ignore values outside of the .text range.
if (!IsValueInTextRange(address)) {
XELOGSDB(XT("Ignoring function outside of .text: %.8X"), address);
return NULL;
}
fn = new FunctionSymbol();
fn->start_address = address;
function_count_++;
symbols_.insert(SymbolMap::value_type(address, fn));
scan_queue_.push_back(fn);
return fn;
}
VariableSymbol* SymbolDatabase::GetOrInsertVariable(uint32_t address) {
VariableSymbol* var = GetVariable(address);
if (var) {
return var;
}
var = new VariableSymbol();
var->address = address;
variable_count_++;
symbols_.insert(SymbolMap::value_type(address, var));
return var;
}
FunctionSymbol* SymbolDatabase::GetFunction(uint32_t address) {
SymbolMap::iterator i = symbols_.find(address);
if (i != symbols_.end() && i->second->symbol_type == Symbol::Function) {
return static_cast<FunctionSymbol*>(i->second);
}
return NULL;
}
VariableSymbol* SymbolDatabase::GetVariable(uint32_t address) {
SymbolMap::iterator i = symbols_.find(address);
if (i != symbols_.end() && i->second->symbol_type == Symbol::Variable) {
return static_cast<VariableSymbol*>(i->second);
}
return NULL;
}
int SymbolDatabase::GetAllVariables(std::vector<VariableSymbol*>& variables) {
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
if (it->second->symbol_type == Symbol::Variable) {
variables.push_back(static_cast<VariableSymbol*>(it->second));
}
}
return 0;
}
int SymbolDatabase::GetAllFunctions(vector<FunctionSymbol*>& functions) {
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
if (it->second->symbol_type == Symbol::Function) {
functions.push_back(static_cast<FunctionSymbol*>(it->second));
}
}
return 0;
}
int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
// Ignore functions already analyzed.
if (fn->blocks.size()) {
return 0;
}
// Ignore kernel thunks.
if (fn->type == FunctionSymbol::Kernel) {
return 0;
}
// Ignore bad inserts?
if (fn->start_address == fn->end_address) {
return 0;
}
// This is a simple basic block analyizer. It walks the start address to the
// end address looking for branches. Each span of instructions between
// branches is considered a basic block, and the blocks are linked up to
// create a CFG for the function. When the last blr (that has no branches
// to after it) is found the function is considered ended. If this is before
// the expected end address then the function address range is split up and
// the second half is treated as another function.
// TODO(benvanik): special branch checks:
// bl to _XamLoaderTerminateTitle should be treated as b
// bl to KeBugCheck should be treated as b, and b KeBugCheck should die
// TODO(benvanik): identify thunks:
// These look like:
// li r5, 0
// [etc]
// b some_function
// Can probably be detected by lack of use of LR?
uint8_t* p = xe_memory_addr(memory_, 0);
if (XEGETUINT32LE(p + fn->start_address) == 0) {
// Function starts with 0x00000000 - we want to skip this and split.
symbols_.erase(fn->start_address);
// Scan ahead until the first non-zero or the end of the valid range.
size_t next_addr = fn->start_address + 4;
while (true) {
if (!IsValueInTextRange(next_addr)) {
// Ran out of the range. Abort.
delete fn;
return 0;
}
if (XEGETUINT32LE(p + next_addr)) {
// Not a zero, maybe valid!
break;
}
next_addr += 4;
}
if (!GetFunction(next_addr + 4)) {
fn->start_address = next_addr;
symbols_.insert(SymbolMap::value_type(fn->start_address, fn));
scan_queue_.push_back(fn);
} else {
delete fn;
}
return 0;
}
XELOGSDB(XT("Analyzing function %.8X..."), fn->start_address);
// Set a default name, if it hasn't been named already.
if (!fn->name()) {
char name[32];
xesnprintfa(name, XECOUNT(name), "sub_%.8X", fn->start_address);
fn->set_name(name);
}
// Set type, if needed. We assume user if not set.
if (fn->type == FunctionSymbol::Unknown) {
fn->type = FunctionSymbol::User;
}
InstrData i;
FunctionBlock* block = NULL;
uint32_t furthest_target = fn->start_address;
uint32_t addr = fn->start_address;
while (true) {
i.code = XEGETUINT32BE(p + addr);
i.type = ppc::GetInstrType(i.code);
i.address = addr;
// If we fetched 0 assume that we somehow hit one of the awesome
// 'no really we meant to end after that bl' functions.
if (!i.code) {
XELOGSDB(XT("function end %.8X (0x00000000 read)"), addr);
break;
}
// Create a new basic block, if needed.
if (!block) {
block = new FunctionBlock();
block->start_address = addr;
block->end_address = addr;
fn->blocks.insert(std::pair<uint32_t, FunctionBlock*>(
block->start_address, block));
}
bool ends_block = false;
bool ends_fn = false;
if (!i.type) {
// Invalid instruction.
// We can just ignore it because there's (very little)/no chance it'll
// affect flow control.
XELOGSDB(XT("Invalid instruction at %.8X: %.8X"), addr, i.code);
} else if (i.code == 0x4E800020) {
// blr -- unconditional branch to LR.
// This is generally a return.
block->outgoing_type = FunctionBlock::kTargetLR;
if (furthest_target > addr) {
// Remaining targets within function, not end.
XELOGSDB(XT("ignoring blr %.8X (branch to %.8X)"), addr,
furthest_target);
} else {
// Function end point.
XELOGSDB(XT("function end %.8X"), addr);
ends_fn = true;
}
ends_block = true;
} else if (i.code == 0x4E800420) {
// bctr -- unconditional branch to CTR.
// This is generally a jump to a function pointer (non-return).
block->outgoing_type = FunctionBlock::kTargetCTR;
if (furthest_target > addr) {
// Remaining targets within function, not end.
XELOGSDB(XT("ignoring bctr %.8X (branch to %.8X)"), addr,
furthest_target);
} else {
// Function end point.
XELOGSDB(XT("function end %.8X"), addr);
ends_fn = true;
}
ends_block = true;
} else if (i.type->opcode == 0x48000000) {
// b/ba/bl/bla
uint32_t target = XEEXTS26(i.I.LI << 2) + (i.I.AA ? 0 : (int32_t)addr);
block->outgoing_address = target;
if (i.I.LK) {
XELOGSDB(XT("bl %.8X -> %.8X"), addr, target);
// Queue call target if needed.
GetOrInsertFunction(target);
} else {
XELOGSDB(XT("b %.8X -> %.8X"), addr, target);
// If the target is back into the function and there's no further target
// we are at the end of a function.
if (target >= fn->start_address &&
target < addr && furthest_target <= addr) {
XELOGSDB(XT("function end %.8X (back b)"), addr);
ends_fn = true;
}
// If the target is a __restgprlr_* method it's the end of a function.
// Note that sometimes functions stick this in a basic block *inside*
// of the function somewhere, so ensure we don't have any branches over
// it.
if (!ends_fn &&
furthest_target <= addr && IsRestGprLr(target)) {
XELOGSDB(XT("function end %.8X (__restgprlr_*)"), addr);
ends_fn = true;
}
if (!ends_fn) {
furthest_target = MAX(furthest_target, target);
// TODO(benvanik): perhaps queue up for a speculative check? I think
// we are running over tail-call functions here that branch to
// somewhere else.
//GetOrInsertFunction(target);
}
}
ends_block = true;
} else if (i.type->opcode == 0x40000000) {
// bc/bca/bcl/bcla
uint32_t target = XEEXTS16(i.B.BD << 2) + (i.B.AA ? 0 : (int32_t)addr);
block->outgoing_address = target;
if (i.B.LK) {
XELOGSDB(XT("bcl %.8X -> %.8X"), addr, target);
// Queue call target if needed.
// TODO(benvanik): see if this is correct - not sure anyone makes
// function calls with bcl.
//GetOrInsertFunction(target);
} else {
XELOGSDB(XT("bc %.8X -> %.8X"), addr, target);
// TODO(benvanik): GetOrInsertFunction? it's likely a BB
furthest_target = MAX(furthest_target, target);
}
ends_block = true;
} else if (i.type->opcode == 0x4C000020) {
// bclr/bclrl
block->outgoing_type = FunctionBlock::kTargetLR;
if (i.XL.LK) {
XELOGSDB(XT("bclrl %.8X"), addr);
} else {
XELOGSDB(XT("bclr %.8X"), addr);
}
ends_block = true;
} else if (i.type->opcode == 0x4C000420) {
// bcctr/bcctrl
block->outgoing_type = FunctionBlock::kTargetCTR;
if (i.XL.LK) {
XELOGSDB(XT("bcctrl %.8X"), addr);
} else {
XELOGSDB(XT("bcctr %.8X"), addr);
}
ends_block = true;
}
block->end_address = addr;
if (ends_block) {
// This instruction is the end of a basic block.
// Finish up the one we are working on. The next loop around will create
// a new one to scribble into.
block = NULL;
}
if (ends_fn) {
break;
}
addr += 4;
if (fn->end_address && addr > fn->end_address) {
// Hmm....
XELOGSDB(XT("Ran over function bounds! %.8X-%.8X"),
fn->start_address, fn->end_address);
break;
}
}
if (addr + 4 < fn->end_address) {
// Ran under the expected value - since we probably got the initial bounds
// from someplace valid (like method hints) this may indicate an error.
// It's also possible that we guessed in hole-filling and there's another
// function below this one.
XELOGSDB(XT("Function ran under: %.8X-%.8X ended at %.8X"),
fn->start_address, fn->end_address, addr + 4);
}
fn->end_address = addr;
// If there's spare bits at the end, split the function.
// TODO(benvanik): splitting?
// TODO(benvanik): find and record stack information
// - look for __savegprlr_* and __restgprlr_*
// - if present, flag function as needing a stack
// - record prolog/epilog lengths/stack size/etc
XELOGSDB(XT("Finished analyzing %.8X"), fn->start_address);
return 0;
}
int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
// Find variable accesses.
// TODO(benvanik): data analysis to find variable accesses.
// A list of function targets that were undefined.
// This will run another analysis pass and it'd be best to avoid this.
std::vector<uint32_t> new_fns;
// For each basic block:
// - find outgoing target block or function
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn->blocks.begin();
it != fn->blocks.end(); ++it) {
FunctionBlock* block = it->second;
// If we have some address try to see what it is.
if (block->outgoing_address) {
if (block->outgoing_address >= fn->start_address &&
block->outgoing_address <= fn->end_address) {
// Branch into a block in this function.
block->outgoing_type = FunctionBlock::kTargetBlock;
block->outgoing_block = fn->GetBlock(block->outgoing_address);
if (!block->outgoing_block) {
// Block target not found - we may need to split.
block->outgoing_block = fn->SplitBlock(block->outgoing_address);
}
if (!block->outgoing_block) {
XELOGE(XT("block target not found: %.8X"), block->outgoing_address);
XEASSERTALWAYS();
}
} else {
// Function call.
block->outgoing_type = FunctionBlock::kTargetFunction;
block->outgoing_function = GetFunction(block->outgoing_address);
if (!block->outgoing_function) {
XELOGE(XT("call target not found: %.8X -> %.8X"),
block->end_address, block->outgoing_address);
new_fns.push_back(block->outgoing_address);
}
}
}
}
if (new_fns.size()) {
XELOGW(XT("Repeat analysis required to find %d new functions"),
(uint32_t)new_fns.size());
for (std::vector<uint32_t>::iterator it = new_fns.begin();
it != new_fns.end(); ++it) {
GetOrInsertFunction(*it);
}
return 1;
}
return 0;
}
namespace {
typedef struct {
uint32_t start_address;
uint32_t end_address;
} HoleInfo;
}
bool SymbolDatabase::FillHoles() {
// If 4b, check if 0x00000000 and ignore (alignment padding)
// If 8b, check if first value is within .text and ignore (EH entry)
// Else, add to scan queue as function?
std::vector<HoleInfo> holes;
std::vector<uint32_t> ees;
uint32_t previous = 0;
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
switch (it->second->symbol_type) {
case Symbol::Function:
{
FunctionSymbol* fn = static_cast<FunctionSymbol*>(it->second);
if (previous && (int)(fn->start_address - previous) > 0) {
// Hole!
uint32_t* p = (uint32_t*)xe_memory_addr(memory_, previous);
size_t hole_length = fn->start_address - previous;
if (hole_length == 4) {
// Likely a pointer or 0.
if (*p == 0) {
// Skip - just a zero.
} else if (IsValueInTextRange(XEGETUINT32BE(p))) {
// An address - probably an indirection data value.
}
} else if (hole_length == 8) {
// Possibly an exception handler entry.
// They look like [some value in .text] + [some pointer].
if (*p == 0 || IsValueInTextRange(XEGETUINT32BE(p))) {
// Skip!
ees.push_back(previous);
} else {
// Probably legit.
HoleInfo hole_info = {previous, fn->start_address};
holes.push_back(hole_info);
}
} else {
// Probably legit.
HoleInfo hole_info = {previous, fn->start_address};
holes.push_back(hole_info);
}
}
previous = fn->end_address + 4;
}
break;
case Symbol::Variable:
case Symbol::ExceptionEntry:
break;
}
}
for (std::vector<uint32_t>::iterator it = ees.begin(); it != ees.end();
++it) {
ExceptionEntrySymbol* ee = GetOrInsertExceptionEntry(*it);
ee->function = GetFunction(ee->address + 8);
if (ee->function) {
ee->function->ee = ee;
}
uint32_t* p = (uint32_t*)xe_memory_addr(memory_, ee->address);
uint32_t handler_addr = XEGETUINT32BE(p);
if (handler_addr) {
GetOrInsertFunction(handler_addr);
}
uint32_t data_addr = XEGETUINT32BE(p + 1);
if (data_addr) {
VariableSymbol* var = GetOrInsertVariable(data_addr);
char name[128];
if (ee->function) {
xesnprintfa(name, XECOUNT(name), "__ee_data_%s", ee->function->name());
} else {
xesnprintfa(name, XECOUNT(name), "__ee_data_%.8X", *it);
}
var->set_name(name);
}
}
bool any_functions_added = false;
for (std::vector<HoleInfo>::iterator it = holes.begin(); it != holes.end();
++it) {
FunctionSymbol* fn = GetOrInsertFunction(it->start_address);
if (!fn->end_address) {
fn->end_address = it->end_address;
any_functions_added = true;
}
}
return any_functions_added;
}
int SymbolDatabase::FlushQueue() {
while (scan_queue_.size()) {
FunctionSymbol* fn = scan_queue_.front();
scan_queue_.pop_front();
if (AnalyzeFunction(fn)) {
XELOGSDB(XT("Aborting analysis!"));
return 1;
}
}
return 0;
}
bool SymbolDatabase::IsRestGprLr(uint32_t addr) {
FunctionSymbol* fn = GetFunction(addr);
return fn && (fn->flags & FunctionSymbol::kFlagRestGprLr);
}
void SymbolDatabase::ReadMap(const char* file_name) {
std::ifstream infile(file_name);
// Skip until ' Address'. Skip the next blank line.
std::string line;
while (std::getline(infile, line)) {
if (line.find(" Address") == 0) {
// Skip the next line.
std::getline(infile, line);
break;
}
}
std::stringstream sstream;
std::string ignore;
std::string name;
std::string addr_str;
std::string type_str;
while (std::getline(infile, line)) {
// Remove newline.
while (line.size() &&
(line[line.size() - 1] == '\r' ||
line[line.size() - 1] == '\n')) {
line.erase(line.end() - 1);
}
// End when we hit the first whitespace.
if (line.size() == 0) {
break;
}
// Line is [ws][ignore][ws][name][ws][hex addr][ws][(f)][ws][library]
sstream.clear();
sstream.str(line);
sstream >> std::ws;
sstream >> ignore;
sstream >> std::ws;
sstream >> name;
sstream >> std::ws;
sstream >> addr_str;
sstream >> std::ws;
sstream >> type_str;
uint32_t addr = (uint32_t)strtol(addr_str.c_str(), NULL, 16);
if (!addr) {
continue;
}
Symbol* symbol = GetSymbol(addr);
if (symbol) {
// Symbol found - set name.
// We could check the type, but it's not needed.
symbol->set_name(name.c_str());
} else {
if (type_str == "f") {
// Function was not found via analysis.
// We don't want to add it here as that would make us require maps to
// get working.
XELOGSDB(XT("MAP DIFF: function %.8X %s not found during analysis"),
addr, name.c_str());
} else {
// Add a new variable.
// This is just helpful, but changes no behavior.
VariableSymbol* var = GetOrInsertVariable(addr);
var->set_name(name.c_str());
}
}
}
}
void SymbolDatabase::WriteMap(const char* file_name) {
FILE* file = fopen(file_name, "wt");
Dump(file);
fclose(file);
}
void SymbolDatabase::Dump(FILE* file) {
uint32_t previous = 0;
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
switch (it->second->symbol_type) {
case Symbol::Function:
{
FunctionSymbol* fn = static_cast<FunctionSymbol*>(it->second);
if (previous && (int)(fn->start_address - previous) > 0) {
if (fn->start_address - previous > 4 ||
*((uint32_t*)xe_memory_addr(memory_, previous)) != 0) {
fprintf(file, "%.8X-%.8X (%5d) h\n", previous, fn->start_address,
fn->start_address - previous);
}
}
fprintf(file, "%.8X-%.8X (%5d) f %s\n",
fn->start_address,
fn->end_address + 4,
fn->end_address - fn->start_address + 4,
fn->name() ? fn->name() : "<unknown>");
previous = fn->end_address + 4;
DumpFunctionBlocks(file, fn);
}
break;
case Symbol::Variable:
{
VariableSymbol* var = static_cast<VariableSymbol*>(it->second);
fprintf(file, "%.8X v %s\n", var->address,
var->name() ? var->name() : "<unknown>");
}
break;
case Symbol::ExceptionEntry:
{
ExceptionEntrySymbol* ee = static_cast<ExceptionEntrySymbol*>(
it->second);
fprintf(file, "%.8X-%.8X (%5d) e of %.8X\n",
ee->address, ee->address + 8, 8,
ee->function ? ee->function->start_address : 0);
previous = ee->address + 8 + 4;
}
break;
}
}
}
void SymbolDatabase::DumpFunctionBlocks(FILE* file, FunctionSymbol* fn) {
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn->blocks.begin();
it != fn->blocks.end(); ++it) {
FunctionBlock* block = it->second;
fprintf(file, " bb %.8X-%.8X",
block->start_address, block->end_address + 4);
switch (block->outgoing_type) {
case FunctionBlock::kTargetUnknown:
fprintf(file, " ?\n");
break;
case FunctionBlock::kTargetBlock:
fprintf(file, " branch %.8X\n", block->outgoing_block->start_address);
break;
case FunctionBlock::kTargetFunction:
fprintf(file, " call %.8X %s\n",
block->outgoing_function->start_address,
block->outgoing_function->name());
break;
case FunctionBlock::kTargetLR:
fprintf(file, " branch lr\n");
break;
case FunctionBlock::kTargetCTR:
fprintf(file, " branch ctr\n");
break;
case FunctionBlock::kTargetNone:
fprintf(file, "\n");
break;
}
}
}

View File

@@ -0,0 +1,77 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_SDB_SYMBOL_DATABASE_H_
#define XENIA_CPU_SDB_SYMBOL_DATABASE_H_
#include <xenia/core.h>
#include <list>
#include <map>
#include <vector>
#include <xenia/kernel/export.h>
#include <xenia/cpu/sdb/symbol.h>
namespace xe {
namespace cpu {
namespace sdb {
class SymbolDatabase {
public:
SymbolDatabase(xe_memory_ref memory, kernel::ExportResolver* export_resolver);
virtual ~SymbolDatabase();
virtual int Analyze();
Symbol* GetSymbol(uint32_t address);
ExceptionEntrySymbol* GetOrInsertExceptionEntry(uint32_t address);
FunctionSymbol* GetOrInsertFunction(uint32_t address);
VariableSymbol* GetOrInsertVariable(uint32_t address);
FunctionSymbol* GetFunction(uint32_t address);
VariableSymbol* GetVariable(uint32_t address);
int GetAllVariables(std::vector<VariableSymbol*>& variables);
int GetAllFunctions(std::vector<FunctionSymbol*>& functions);
void ReadMap(const char* file_name);
void WriteMap(const char* file_name);
void Dump(FILE* file);
void DumpFunctionBlocks(FILE* file, FunctionSymbol* fn);
protected:
typedef std::map<uint32_t, Symbol*> SymbolMap;
typedef std::list<FunctionSymbol*> FunctionList;
int AnalyzeFunction(FunctionSymbol* fn);
int CompleteFunctionGraph(FunctionSymbol* fn);
bool FillHoles();
int FlushQueue();
bool IsRestGprLr(uint32_t addr);
virtual uint32_t GetEntryPoint() = 0;
virtual bool IsValueInTextRange(uint32_t value) = 0;
xe_memory_ref memory_;
kernel::ExportResolver* export_resolver_;
size_t function_count_;
size_t variable_count_;
SymbolMap symbols_;
FunctionList scan_queue_;
};
} // namespace sdb
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_SDB_SYMBOL_DATABASE_H_

View File

@@ -0,0 +1,307 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/sdb/xex_symbol_database.h>
#include <xenia/cpu/ppc/instr.h>
using namespace std;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
namespace {
// IMAGE_CE_RUNTIME_FUNCTION_ENTRY
// http://msdn.microsoft.com/en-us/library/ms879748.aspx
typedef struct IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY_t {
uint32_t FuncStart; // Virtual address
union {
struct {
uint32_t PrologLen : 8; // # of prolog instructions (size = x4)
uint32_t FuncLen : 22; // # of instructions total (size = x4)
uint32_t ThirtyTwoBit : 1; // Always 1
uint32_t ExceptionFlag : 1; // 1 if PDATA_EH in .text -- unknown if used
} Flags;
uint32_t FlagsValue; // To make byte swapping easier
};
} IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY;
class PEMethodInfo {
public:
uint32_t address;
size_t total_length; // in bytes
size_t prolog_length; // in bytes
};
}
XexSymbolDatabase::XexSymbolDatabase(
xe_memory_ref memory, ExportResolver* export_resolver, xe_xex2_ref xex) :
SymbolDatabase(memory, export_resolver) {
xex_ = xe_xex2_retain(xex);
}
XexSymbolDatabase::~XexSymbolDatabase() {
xe_xex2_release(xex_);
}
int XexSymbolDatabase::Analyze() {
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
// Find __savegprlr_* and __restgprlr_*.
FindGplr();
// Add each import thunk.
for (size_t n = 0; n < header->import_library_count; n++) {
AddImports(&header->import_libraries[n]);
}
// Add each export root.
// TODO(benvanik): exports.
// - insert fn or variable
// - queue fn
// Add method hints, if available.
// Not all XEXs have these.
AddMethodHints();
return SymbolDatabase::Analyze();
}
int XexSymbolDatabase::FindGplr() {
// Special stack save/restore functions.
// __savegprlr_14 to __savegprlr_31
// __restgprlr_14 to __restgprlr_31
// http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/ppc/xxx.s.htm
// It'd be nice to stash these away and mark them as such to allow for
// special codegen.
static const uint32_t code_values[] = {
0x68FFC1F9, // __savegprlr_14
0x70FFE1F9, // __savegprlr_15
0x78FF01FA, // __savegprlr_16
0x80FF21FA, // __savegprlr_17
0x88FF41FA, // __savegprlr_18
0x90FF61FA, // __savegprlr_19
0x98FF81FA, // __savegprlr_20
0xA0FFA1FA, // __savegprlr_21
0xA8FFC1FA, // __savegprlr_22
0xB0FFE1FA, // __savegprlr_23
0xB8FF01FB, // __savegprlr_24
0xC0FF21FB, // __savegprlr_25
0xC8FF41FB, // __savegprlr_26
0xD0FF61FB, // __savegprlr_27
0xD8FF81FB, // __savegprlr_28
0xE0FFA1FB, // __savegprlr_29
0xE8FFC1FB, // __savegprlr_30
0xF0FFE1FB, // __savegprlr_31
0xF8FF8191,
0x2000804E,
0x68FFC1E9, // __restgprlr_14
0x70FFE1E9, // __restgprlr_15
0x78FF01EA, // __restgprlr_16
0x80FF21EA, // __restgprlr_17
0x88FF41EA, // __restgprlr_18
0x90FF61EA, // __restgprlr_19
0x98FF81EA, // __restgprlr_20
0xA0FFA1EA, // __restgprlr_21
0xA8FFC1EA, // __restgprlr_22
0xB0FFE1EA, // __restgprlr_23
0xB8FF01EB, // __restgprlr_24
0xC0FF21EB, // __restgprlr_25
0xC8FF41EB, // __restgprlr_26
0xD0FF61EB, // __restgprlr_27
0xD8FF81EB, // __restgprlr_28
0xE0FFA1EB, // __restgprlr_29
0xE8FFC1EB, // __restgprlr_30
0xF0FFE1EB, // __restgprlr_31
0xF8FF8181,
0xA603887D,
0x2000804E,
};
uint32_t gplr_start = 0;
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
for (size_t n = 0, i = 0; n < header->section_count; n++) {
const xe_xex2_section_t* section = &header->sections[n];
const size_t start_address =
header->exe_address + (i * xe_xex2_section_length);
const size_t end_address =
start_address + (section->info.page_count * xe_xex2_section_length);
if (section->info.type == XEX_SECTION_CODE) {
gplr_start = xe_memory_search_aligned(
memory_, start_address, end_address,
code_values, XECOUNT(code_values));
if (gplr_start) {
break;
}
}
i += section->info.page_count;
}
if (!gplr_start) {
return 0;
}
// Add function stubs.
char name[32];
uint32_t address = gplr_start;
for (int n = 14; n <= 31; n++) {
xesnprintfa(name, XECOUNT(name), "__savegprlr_%d", n);
FunctionSymbol* fn = GetOrInsertFunction(address);
fn->end_address = fn->start_address + (31 - n) * 4 + 2 * 4;
fn->set_name(name);
fn->type = FunctionSymbol::User;
fn->flags |= FunctionSymbol::kFlagSaveGprLr;
address += 4;
}
address = gplr_start + 20 * 4;
for (int n = 14; n <= 31; n++) {
xesnprintfa(name, XECOUNT(name), "__restgprlr_%d", n);
FunctionSymbol* fn = GetOrInsertFunction(address);
fn->end_address = fn->start_address + (31 - n) * 4 + 3 * 4;
fn->set_name(name);
fn->type = FunctionSymbol::User;
fn->flags |= FunctionSymbol::kFlagRestGprLr;
address += 4;
}
return 0;
}
int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
xe_xex2_import_info_t* import_infos;
size_t import_info_count;
if (xe_xex2_get_import_infos(xex_, library, &import_infos,
&import_info_count)) {
return 1;
}
char name[128];
for (size_t n = 0; n < import_info_count; n++) {
const xe_xex2_import_info_t* info = &import_infos[n];
KernelExport* kernel_export = export_resolver_->GetExportByOrdinal(
library->name, info->ordinal);
VariableSymbol* var = GetOrInsertVariable(info->value_address);
if (kernel_export) {
if (info->thunk_address) {
xesnprintfa(name, XECOUNT(name), "__imp_%s", kernel_export->name);
} else {
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
}
} else {
xesnprintfa(name, XECOUNT(name), "__imp_%s_%.3X", library->name,
info->ordinal);
}
var->set_name(name);
var->kernel_export = kernel_export;
if (info->thunk_address) {
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
fn->end_address = fn->start_address + 16 - 4;
fn->type = FunctionSymbol::Kernel;
fn->kernel_export = kernel_export;
if (kernel_export) {
xesnprintfa(name, XECOUNT(name), "%s", kernel_export->name);
} else {
xesnprintfa(name, XECOUNT(name), "__kernel_%s_%.3X", library->name,
info->ordinal);
}
fn->set_name(name);
}
}
xe_free(import_infos);
return 0;
}
int XexSymbolDatabase::AddMethodHints() {
uint8_t* mem = xe_memory_addr(memory_, 0);
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
// Find pdata, which contains the exception handling entries.
const PESection* pdata = xe_xex2_get_pe_section(xex_, ".pdata");
if (!pdata) {
// No exception data to go on.
return 0;
}
// Resolve.
const uint8_t* p = mem + pdata->address;
// Entry count = pdata size / sizeof(entry).
size_t entry_count = pdata->size / sizeof(IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY);
if (!entry_count) {
// Empty?
return 0;
}
// Allocate output.
PEMethodInfo* method_infos = (PEMethodInfo*)xe_calloc(
entry_count * sizeof(PEMethodInfo));
if (!method_infos) {
return 0;
}
// Parse entries.
// NOTE: entries are in memory as big endian, so pull them out and swap the
// values before using them.
entry = (const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY*)p;
IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY temp_entry;
for (size_t n = 0; n < entry_count; n++, entry++) {
PEMethodInfo* method_info = &method_infos[n];
method_info->address = XESWAP32BE(entry->FuncStart);
// The bitfield needs to be swapped by hand.
temp_entry.FlagsValue = XESWAP32BE(entry->FlagsValue);
method_info->total_length = temp_entry.Flags.FuncLen * 4;
method_info->prolog_length = temp_entry.Flags.PrologLen * 4;
}
for (size_t n = 0; n < entry_count; n++) {
PEMethodInfo* method_info = &method_infos[n];
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
fn->end_address = method_info->address + method_info->total_length - 4;
fn->type = FunctionSymbol::User;
// TODO(benvanik): something with prolog_length?
}
xe_free(method_infos);
return 0;
}
uint32_t XexSymbolDatabase::GetEntryPoint() {
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
return header->exe_entry_point;
};
bool XexSymbolDatabase::IsValueInTextRange(uint32_t value) {
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
for (size_t n = 0, i = 0; n < header->section_count; n++) {
const xe_xex2_section_t* section = &header->sections[n];
const size_t start_address =
header->exe_address + (i * xe_xex2_section_length);
const size_t end_address =
start_address + (section->info.page_count * xe_xex2_section_length);
if (value >= start_address && value < end_address) {
return section->info.type == XEX_SECTION_CODE;
}
i += section->info.page_count;
}
return false;
}

View File

@@ -0,0 +1,49 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
#define XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_
#include <xenia/cpu/sdb/symbol_database.h>
#include <xenia/kernel/xex2.h>
namespace xe {
namespace cpu {
namespace sdb {
class XexSymbolDatabase : public SymbolDatabase {
public:
XexSymbolDatabase(xe_memory_ref memory,
kernel::ExportResolver* export_resolver,
xe_xex2_ref xex);
virtual ~XexSymbolDatabase();
virtual int Analyze();
private:
int FindGplr();
int AddImports(const xe_xex2_import_library_t *library);
int AddMethodHints();
virtual uint32_t GetEntryPoint();
virtual bool IsValueInTextRange(uint32_t value);
xe_xex2_ref xex_;
};
} // namespace sdb
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_SDB_XEX_SYMBOL_DATABASE_H_

View File

@@ -0,0 +1,23 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'cpu-private.h',
'cpu.cc',
'cpu.h',
'exec_module.cc',
'exec_module.h',
'llvm_exports.cc',
'llvm_exports.h',
'ppc.h',
'processor.cc',
'processor.h',
'thread_state.cc',
'thread_state.h',
],
'includes': [
'codegen/sources.gypi',
'ppc/sources.gypi',
'sdb/sources.gypi',
],
}

View File

@@ -0,0 +1,47 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/thread_state.h>
#include <xenia/core/memory.h>
#include <xenia/cpu/processor.h>
using namespace xe;
using namespace xe::cpu;
ThreadState::ThreadState(
Processor* processor,
uint32_t stack_size, uint32_t thread_state_address) :
stack_size_(stack_size), thread_state_address_(thread_state_address) {
memory_ = processor->memory();
stack_address_ = xe_memory_heap_alloc(memory_, 0, stack_size, 0);
xe_zero_struct(&ppc_state_, sizeof(ppc_state_));
// Stash pointers to common structures that callbacks may need.
ppc_state_.membase = xe_memory_addr(memory_, 0);
ppc_state_.processor = processor;
ppc_state_.thread_state = this;
// Set initial registers.
ppc_state_.r[1] = stack_address_;
ppc_state_.r[13] = thread_state_address_;
}
ThreadState::~ThreadState() {
xe_memory_heap_free(memory_, stack_address_, 0);
xe_memory_release(memory_);
}
xe_ppc_state_t* ThreadState::ppc_state() {
return &ppc_state_;
}

View File

@@ -0,0 +1,49 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_THREAD_STATE_H_
#define XENIA_CPU_THREAD_STATE_H_
#include <xenia/core.h>
#include <xenia/cpu/ppc.h>
namespace xe {
namespace cpu {
class Processor;
class ThreadState {
public:
ThreadState(Processor* processor,
uint32_t stack_size, uint32_t thread_state_address);
~ThreadState();
xe_ppc_state_t* ppc_state();
private:
uint32_t stack_size_;
uint32_t thread_state_address;
xe_memory_ref memory_;
uint32_t stack_address_;
uint32_t thread_state_address_;
xe_ppc_state_t ppc_state_;
};
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_THREAD_STATE_H_

Binary file not shown.

View File

@@ -0,0 +1,39 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
/**
* This file is compiled with clang to produce LLVM bitcode.
* When the emulator goes to build a full module it then imports this code into
* the generated module to provide globals/other shared values.
*
* Changes to this file require building a new version and checking it into the
* repo on a machine that has clang.
*
* # rebuild the xethunk.bc/.ll files:
* xb xethunk
*/
// NOTE: only headers in this directory should be included.
#include "xethunk.h"
// Global memory base.
// Dereference + PPC address to manipulate memory. Note that it's stored in
// big-endian!
extern char* xe_memory_base;
int xe_module_init() {
// TODO(benvanik): setup call table, etc?
return 0;
}
void xe_module_uninit() {
}

View File

@@ -0,0 +1,20 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
/**
* This file is shared between xethunk and the loader to pass structures
* between the two. Since this file is compiled with the LLVM clang it cannot
* include any other files.
*/
#ifndef XENIA_CPU_XETHUNK_H_
#define XENIA_CPU_XETHUNK_H_
#endif // XENIA_CPU_XETHUNK_H_

View File

@@ -0,0 +1,11 @@
; ModuleID = 'src/cpu/xethunk/xethunk.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"
define i32 @xe_module_init() nounwind uwtable ssp {
ret i32 0
}
define void @xe_module_uninit() nounwind uwtable ssp {
ret void
}

36
src/xenia/dbg/client.cc Normal file
View File

@@ -0,0 +1,36 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/dbg/client.h>
#include <xenia/dbg/debugger.h>
using namespace xe;
using namespace xe::dbg;
Client::Client(Debugger* debugger) :
debugger_(debugger) {
debugger_->AddClient(this);
}
Client::~Client() {
debugger_->RemoveClient(this);
}
void Client::OnMessage(const uint8_t* data, size_t length) {
debugger_->Dispatch(this, data, length);
}
void Client::Write(uint8_t* buffer, size_t length) {
uint8_t* buffers[] = {buffer};
size_t lengths[] = {length};
Write(buffers, lengths, 1);
}

44
src/xenia/dbg/client.h Normal file
View File

@@ -0,0 +1,44 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_DBG_CLIENT_H_
#define XENIA_KERNEL_DBG_CLIENT_H_
#include <xenia/common.h>
#include <xenia/core.h>
namespace xe {
namespace dbg {
class Debugger;
class Client {
public:
Client(Debugger* debugger);
virtual ~Client();
virtual int Setup() = 0;
void OnMessage(const uint8_t* data, size_t length);
void Write(uint8_t* buffer, size_t length);
virtual void Write(uint8_t** buffers, size_t* lengths, size_t count) = 0;
protected:
Debugger* debugger_;
};
} // namespace dbg
} // namespace xe
#endif // XENIA_KERNEL_DBG_CLIENT_H_

View File

@@ -0,0 +1,26 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/dbg/content_source.h>
using namespace xe;
using namespace xe::dbg;
ContentSource::ContentSource(Debugger* debugger, uint32_t source_id) :
debugger_(debugger), source_id_(source_id) {
}
ContentSource::~ContentSource() {
}
uint32_t ContentSource::source_id() {
return source_id_;
}

View File

@@ -0,0 +1,43 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_DBG_CONTENT_SOURCE_H_
#define XENIA_KERNEL_DBG_CONTENT_SOURCE_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/dbg/client.h>
namespace xe {
namespace dbg {
class ContentSource {
public:
ContentSource(Debugger* debugger, uint32_t source_id);
virtual ~ContentSource();
uint32_t source_id();
virtual int Dispatch(Client* client, uint8_t type, uint32_t request_id,
const uint8_t* data, size_t length) = 0;
protected:
Debugger* debugger_;
uint32_t source_id_;
};
} // namespace dbg
} // namespace xe
#endif // XENIA_KERNEL_DBG_CONTENT_SOURCE_H_

125
src/xenia/dbg/debugger.cc Normal file
View File

@@ -0,0 +1,125 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/dbg/debugger.h>
#include <gflags/gflags.h>
#include <xenia/dbg/content_source.h>
#include <xenia/dbg/ws_listener.h>
using namespace xe;
using namespace xe::dbg;
DEFINE_bool(wait_for_debugger, false,
"Whether to wait for the debugger to attach before launching.");
DEFINE_int32(remote_debug_port, 6200,
"Websocket port to listen for debugger connections on.");
Debugger::Debugger(xe_pal_ref pal) {
pal_ = xe_pal_retain(pal);
listener_ = auto_ptr<Listener>(new WsListener(
this, pal_, FLAGS_remote_debug_port));
}
Debugger::~Debugger() {
for (std::vector<Client*>::iterator it = clients_.begin();
it != clients_.end(); ++it) {
delete *it;
}
clients_.clear();
for (std::map<uint32_t, ContentSource*>::iterator it =
content_sources_.begin(); it != content_sources_.end(); ++it) {
delete it->second;
}
content_sources_.clear();
xe_pal_release(pal_);
}
void Debugger::RegisterContentSource(ContentSource* content_source) {
content_sources_.insert(std::pair<uint32_t, ContentSource*>(
content_source->source_id(), content_source));
}
int Debugger::Startup() {
// Start listener.
// This may launch a thread and such.
if (listener_->Setup()) {
return 1;
}
// If desired, wait until the first client connects.
if (FLAGS_wait_for_debugger) {
XELOGI(XT("Waiting for debugger on port %d..."), FLAGS_remote_debug_port);
if (listener_->WaitForClient()) {
return 1;
}
XELOGI(XT("Debugger attached, continuing..."));
}
return 0;
}
void Debugger::Broadcast(uint32_t source_id,
const uint8_t* data, size_t length) {
uint32_t header[] = {
0x00000001,
source_id,
0,
length,
};
uint8_t* buffers[] = {
(uint8_t*)header,
(uint8_t*)data,
};
size_t lengths[] = {
sizeof(header),
length,
};
for (std::vector<Client*>::iterator it = clients_.begin();
it != clients_.end(); ++it) {
Client* client = *it;
client->Write(buffers, lengths, XECOUNT(buffers));
}
}
void Debugger::AddClient(Client* client) {
clients_.push_back(client);
}
void Debugger::RemoveClient(Client* client) {
for (std::vector<Client*>::iterator it = clients_.begin();
it != clients_.end(); ++it) {
if (*it == client) {
clients_.erase(it);
return;
}
}
}
int Debugger::Dispatch(Client* client, const uint8_t* data, size_t length) {
uint32_t type = XEGETUINT32LE(data + 0);
uint32_t source_id = XEGETUINT32LE(data + 4);
uint32_t request_id = XEGETUINT32LE(data + 8);
uint32_t size = XEGETUINT32LE(data + 12);
std::map<uint32_t, ContentSource*>::iterator it =
content_sources_.find(source_id);
if (it == content_sources_.end()) {
XELOGW(XT("Content source %d not found, ignoring message"), source_id);
return 1;
}
return it->second->Dispatch(client, type, request_id, data + 16, size);
}

59
src/xenia/dbg/debugger.h Normal file
View File

@@ -0,0 +1,59 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_DBG_DEBUGGER_H_
#define XENIA_KERNEL_DBG_DEBUGGER_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <map>
#include <vector>
namespace xe {
namespace dbg {
class Client;
class ContentSource;
class Listener;
class Debugger {
public:
Debugger(xe_pal_ref pal);
virtual ~Debugger();
void RegisterContentSource(ContentSource* content_source);
int Startup();
void Broadcast(uint32_t source_id, const uint8_t* data, size_t length);
private:
void AddClient(Client* client);
void RemoveClient(Client* client);
int Dispatch(Client* client, const uint8_t* data, size_t length);
friend class Client;
private:
xe_pal_ref pal_;
auto_ptr<Listener> listener_;
std::vector<Client*> clients_;
std::map<uint32_t, ContentSource*> content_sources_;
};
} // namespace dbg
} // namespace xe
#endif // XENIA_KERNEL_DBG_DEBUGGER_H_

24
src/xenia/dbg/listener.cc Normal file
View File

@@ -0,0 +1,24 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/dbg/listener.h>
using namespace xe;
using namespace xe::dbg;
Listener::Listener(Debugger* debugger, xe_pal_ref pal) :
debugger_(debugger) {
pal_ = xe_pal_retain(pal);
}
Listener::~Listener() {
xe_pal_release(pal_);
}

42
src/xenia/dbg/listener.h Normal file
View File

@@ -0,0 +1,42 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_DBG_LISTENER_H_
#define XENIA_KERNEL_DBG_LISTENER_H_
#include <xenia/common.h>
#include <xenia/core.h>
namespace xe {
namespace dbg {
class Debugger;
class Listener {
public:
Listener(Debugger* debugger, xe_pal_ref pal);
virtual ~Listener();
virtual int Setup() = 0;
virtual int WaitForClient() = 0;
protected:
Debugger* debugger_;
xe_pal_ref pal_;
};
} // namespace dbg
} // namespace xe
#endif // XENIA_KERNEL_DBG_LISTENER_H_

View File

@@ -0,0 +1,17 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'client.cc',
'client.h',
'content_source.cc',
'content_source.h',
'debugger.cc',
'debugger.h',
'listener.cc',
'listener.h',
'ws_client.cc',
'ws_client.h',
'ws_listener.cc',
'ws_listener.h',
],
}

397
src/xenia/dbg/ws_client.cc Normal file
View File

@@ -0,0 +1,397 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/dbg/ws_client.h>
#include <xenia/dbg/debugger.h>
#include <fcntl.h>
#include <poll.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <wslay/wslay.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
using namespace xe;
using namespace xe::dbg;
WsClient::WsClient(Debugger* debugger, int socket_id) :
Client(debugger),
socket_id_(socket_id) {
mutex_ = xe_mutex_alloc(1000);
int notify_ids[2];
socketpair(PF_LOCAL, SOCK_STREAM, 0, notify_ids);
notify_rd_id_ = notify_ids[0];
notify_wr_id_ = notify_ids[1];
}
WsClient::~WsClient() {
xe_mutex_t* mutex = mutex_;
xe_mutex_lock(mutex);
mutex_ = NULL;
shutdown(socket_id_, SHUT_WR);
close(socket_id_);
socket_id_ = 0;
xe_mutex_unlock(mutex);
xe_mutex_free(mutex);
close(notify_rd_id_);
close(notify_wr_id_);
}
int WsClient::socket_id() {
return socket_id_;
}
int WsClient::Setup() {
// Prep the socket.
int opt_value;
opt_value = 1;
setsockopt(socket_id_, SOL_SOCKET, SO_KEEPALIVE,
&opt_value, sizeof(opt_value));
opt_value = 1;
setsockopt(socket_id_, IPPROTO_TCP, TCP_NODELAY,
&opt_value, sizeof(opt_value));
// TODO(benvanik): win32 support - put simple threads in PAL
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t thread_handle;
int result_code = pthread_create(
&thread_handle,
&attr,
&StartCallbackPthreads,
this);
pthread_attr_destroy(&attr);
return result_code;
}
void* WsClient::StartCallbackPthreads(void* param) {
WsClient* thread = reinterpret_cast<WsClient*>(param);
thread->EventThread();
return 0;
}
namespace {
ssize_t WsClientSendCallback(wslay_event_context_ptr ctx,
const uint8_t* data, size_t len, int flags,
void* user_data) {
WsClient* client = reinterpret_cast<WsClient*>(user_data);
int sflags = 0;
#ifdef MSG_MORE
if (flags & WSLAY_MSG_MORE) {
sflags |= MSG_MORE;
}
#endif // MSG_MORE
ssize_t r;
while ((r = send(client->socket_id(), data, len, sflags)) == -1 &&
errno == EINTR);
if (r == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
} else {
wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
}
}
return r;
}
ssize_t WsClientRecvCallback(wslay_event_context_ptr ctx,
uint8_t* data, size_t len, int flags,
void* user_data) {
WsClient* client = reinterpret_cast<WsClient*>(user_data);
ssize_t r;
while ((r = recv(client->socket_id(), data, len, 0)) == -1 &&
errno == EINTR);
if (r == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
} else {
wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
}
} else if (r == 0) {
wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
r = -1;
}
return r;
}
void WsClientOnMsgCallback(wslay_event_context_ptr ctx,
const struct wslay_event_on_msg_recv_arg* arg,
void* user_data) {
if (wslay_is_ctrl_frame(arg->opcode)) {
// Ignore control frames.
return;
}
WsClient* client = reinterpret_cast<WsClient*>(user_data);
switch (arg->opcode) {
case WSLAY_TEXT_FRAME:
XELOGW(XT("Text frame ignored; use binary messages"));
break;
case WSLAY_BINARY_FRAME:
client->OnMessage(arg->msg, arg->msg_length);
break;
default:
// Unknown opcode - some frame stuff?
break;
}
}
std::string EncodeBase64(const uint8_t* input, size_t length) {
// Good god what a horrible API.
BIO* b64 = BIO_new(BIO_f_base64());
BIO* bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, input, length);
XEIGNORE(BIO_flush(b64));
BUF_MEM* bptr;
BIO_get_mem_ptr(b64, &bptr);
std::string result(bptr->data, bptr->length);
BIO_free_all(b64);
// Strip the last character, which is a \n.
result.erase(result.size() - 1);
return result;
}
}
int WsClient::PerformHandshake() {
std::string headers;
char buffer[4096];
ssize_t r;
while (true) {
while ((r = read(socket_id_, buffer, sizeof(buffer))) == -1 &&
errno == EINTR);
if (r == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
if (!headers.size()) {
// Nothing read yet - spin.
continue;
}
break;
} else {
XELOGE(XT("HTTP header read failure"));
return 1;
}
} else if (r == 0) {
// EOF.
XELOGE(XT("HTTP header EOF"));
return 2;
} else {
headers.append(buffer, buffer + r);
if (headers.size() > 8192) {
XELOGE(XT("HTTP headers exceeded max buffer size"));
return 3;
}
}
}
if (headers.find("\r\n\r\n") == std::string::npos) {
XELOGE(XT("Incomplete HTTP headers: %s"), headers.c_str());
return 1;
}
// Parse the headers to verify its a websocket request.
std::string::size_type keyhdstart;
if (headers.find("Upgrade: websocket\r\n") == std::string::npos ||
headers.find("Connection: Upgrade\r\n") == std::string::npos ||
(keyhdstart = headers.find("Sec-WebSocket-Key: ")) ==
std::string::npos) {
XELOGW(XT("HTTP connection does not contain websocket headers"));
return 2;
}
keyhdstart += 19;
std::string::size_type keyhdend = headers.find("\r\n", keyhdstart);
std::string client_key = headers.substr(keyhdstart, keyhdend - keyhdstart);
std::string accept_key = client_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
uint8_t accept_sha[20];
SHA1((uint8_t*)accept_key.c_str(), accept_key.size(), accept_sha);
accept_key = EncodeBase64(accept_sha, sizeof(accept_sha));
// Write the response to upgrade the connection.
std::string response =
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: " + accept_key + "\r\n"
"\r\n";
size_t write_offset = 0;
size_t write_length = response.size();
while (true) {
while ((r = write(socket_id_, response.c_str() + write_offset,
write_length)) == -1 && errno == EINTR);
if (r == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
} else {
XELOGE(XT("HTTP response write failure"));
return 4;
}
} else {
write_offset += r;
write_length -= r;
if (!write_length) {
break;
}
}
}
return 0;
}
void WsClient::EventThread() {
int r;
// Enable non-blocking IO on the socket.
int flags;
while ((flags = fcntl(socket_id_, F_GETFL, 0)) == -1 &&
errno == EINTR);
if (flags == -1) {
return;
}
while ((r = fcntl(socket_id_, F_SETFL, flags | O_NONBLOCK)) == -1 &&
errno == EINTR);
if (r == -1) {
return;
}
// First run the HTTP handshake.
// This will fail if the connection is not for websockets.
if (PerformHandshake()) {
return;
}
// Prep callbacks.
struct wslay_event_callbacks callbacks = {
WsClientRecvCallback,
WsClientSendCallback,
NULL,
NULL,
NULL,
NULL,
WsClientOnMsgCallback,
};
// Prep the websocket server context.
wslay_event_context_ptr ctx;
wslay_event_context_server_init(&ctx, &callbacks, this);
// Event for waiting on input.
struct pollfd events[2];
xe_zero_struct(&events, sizeof(events));
events[0].fd = socket_id_;
events[0].events = POLLIN;
events[1].fd = notify_rd_id_;
events[1].events = POLLIN;
// Loop forever.
while(wslay_event_want_read(ctx) || wslay_event_want_write(ctx)) {
// Wait on the event.
while ((r = poll(events, XECOUNT(events), -1)) == -1 && errno == EINTR);
if (r == -1) {
break;
}
// Handle any self-generated events to queue messages.
if (events[1].revents) {
uint8_t dummy;
XEIGNORE(recv(notify_rd_id_, &dummy, 1, 0));
xe_mutex_lock(mutex_);
for (std::vector<struct wslay_event_msg>::iterator it =
pending_messages_.begin(); it != pending_messages_.end(); it++) {
struct wslay_event_msg* msg = &*it;
wslay_event_queue_msg(ctx, msg);
}
pending_messages_.clear();
xe_mutex_unlock(mutex_);
events[1].revents = 0;
events[1].events = POLL_IN;
}
// Handle websocket messages.
struct pollfd* event = &events[0];
if (((event->revents & POLLIN) && wslay_event_recv(ctx)) ||
((event->revents & POLLOUT) && wslay_event_send(ctx)) ||
((event->revents & (POLLERR | POLLHUP | POLLNVAL)))) {
// Error handling the event.
XELOGE(XT("Error handling WebSocket data"));
break;
}
event->revents = 0;
event->events = 0;
if (wslay_event_want_read(ctx)) {
event->events |= POLLIN;
}
if (wslay_event_want_write(ctx)) {
event->events |= POLLOUT;
}
}
wslay_event_context_free(ctx);
}
void WsClient::Write(uint8_t** buffers, size_t* lengths, size_t count) {
if (!count) {
return;
}
size_t combined_length;
uint8_t* combined_buffer;
if (count == 1) {
// Single buffer, just copy.
combined_length = lengths[0];
combined_buffer = (uint8_t*)xe_malloc(lengths[0]);
XEIGNORE(xe_copy_memory(combined_buffer, combined_length,
buffers[0], lengths[0]));
} else {
// Multiple buffers, merge.
combined_length = 0;
for (size_t n = 0; n < count; n++) {
combined_length += lengths[n];
}
combined_buffer = (uint8_t*)xe_malloc(combined_length);
for (size_t n = 0, offset = 0; n < count; n++) {
XEIGNORE(xe_copy_memory(
combined_buffer + offset, combined_length - offset,
buffers[n], lengths[n]));
offset += lengths[n];
}
}
struct wslay_event_msg msg = {
WSLAY_BINARY_FRAME,
combined_buffer,
combined_length,
};
xe_mutex_lock(mutex_);
pending_messages_.push_back(msg);
bool needs_signal = pending_messages_.size() == 1;
xe_mutex_unlock(mutex_);
if (needs_signal) {
// Notify the poll().
uint8_t b = 0xFF;
write(notify_wr_id_, &b, 1);
}
}

59
src/xenia/dbg/ws_client.h Normal file
View File

@@ -0,0 +1,59 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_DBG_WS_CLIENT_H_
#define XENIA_KERNEL_DBG_WS_CLIENT_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <vector>
#include <xenia/dbg/client.h>
struct wslay_event_msg;
namespace xe {
namespace dbg {
class WsClient : public Client {
public:
WsClient(Debugger* debugger, int socket_id);
virtual ~WsClient();
int socket_id();
virtual int Setup();
virtual void Write(uint8_t** buffers, size_t* lengths, size_t count);
private:
static void* StartCallbackPthreads(void* param);
int PerformHandshake();
void EventThread();
int socket_id_;
int notify_rd_id_;
int notify_wr_id_;
xe_mutex_t* mutex_;
std::vector<struct wslay_event_msg> pending_messages_;
};
} // namespace dbg
} // namespace xe
#endif // XENIA_KERNEL_DBG_WS_CLIENT_H_

View File

@@ -0,0 +1,95 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/dbg/ws_listener.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <xenia/dbg/ws_client.h>
using namespace xe;
using namespace xe::dbg;
WsListener::WsListener(Debugger* debugger, xe_pal_ref pal, uint32_t port) :
Listener(debugger, pal),
port_(port) {
}
WsListener::~WsListener() {
if (socket_id_) {
close(socket_id_);
}
}
int WsListener::Setup() {
socket_id_ = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket_id_ < 1) {
return 1;
}
int opt_value;
opt_value = 1;
setsockopt(socket_id_, SOL_SOCKET, SO_KEEPALIVE,
&opt_value, sizeof(opt_value));
opt_value = 1;
setsockopt(socket_id_, SOL_SOCKET, SO_REUSEADDR,
&opt_value, sizeof(opt_value));
opt_value = 0;
setsockopt(socket_id_, IPPROTO_TCP, TCP_NODELAY,
&opt_value, sizeof(opt_value));
struct sockaddr_in socket_addr;
socket_addr.sin_family = AF_INET;
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socket_addr.sin_port = htons(port_);
int r = bind(socket_id_, (struct sockaddr*)&socket_addr,
sizeof(socket_addr));
if (r < 0) {
XELOGE(XT("Could not bind listen socket: %d"), errno);
return 1;
}
if (listen(socket_id_, 5) < 0) {
close(socket_id_);
return 1;
}
return 0;
}
int WsListener::WaitForClient() {
// Accept the first connection we get.
struct sockaddr_in client_addr;
socklen_t client_count = sizeof(client_addr);
int client_socket_id = accept(socket_id_, (struct sockaddr*)&client_addr,
&client_count);
if (client_socket_id < 0) {
return 1;
}
int client_ip = client_addr.sin_addr.s_addr;
char client_ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_ip, client_ip_str, XECOUNT(client_ip_str));
XELOGI(XT("Debugger connected from %s"), client_ip_str);
// Create the client object.
// Note that the client will delete itself when done.
WsClient* client = new WsClient(debugger_, client_socket_id);
if (client->Setup()) {
// Client failed to setup - abort.
return 1;
}
return 0;
}

View File

@@ -0,0 +1,45 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_DBG_WS_LISTENER_H_
#define XENIA_KERNEL_DBG_WS_LISTENER_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/dbg/listener.h>
namespace xe {
namespace dbg {
class Debugger;
class WsListener : public Listener {
public:
WsListener(Debugger* debugger, xe_pal_ref pal, uint32_t port);
virtual ~WsListener();
virtual int Setup();
virtual int WaitForClient();
protected:
uint32_t port_;
int socket_id_;
};
} // namespace dbg
} // namespace xe
#endif // XENIA_KERNEL_DBG_WS_LISTENER_H_

15
src/xenia/gpu/gpu.cc Normal file
View File

@@ -0,0 +1,15 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/gpu/gpu.h>
void do_gpu_stuff() {
XELOGGPU(XT("gpu"));
}

18
src/xenia/gpu/gpu.h Normal file
View File

@@ -0,0 +1,18 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_GPU_H_
#define XENIA_GPU_GPU_H_
#include <xenia/common.h>
#include <xenia/core.h>
void do_gpu_stuff();
#endif // XENIA_GPU_GPU_H_

View File

@@ -0,0 +1,7 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'gpu.cc',
'gpu.h',
],
}

View File

@@ -0,0 +1,83 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/kernel/export.h>
using namespace xe;
using namespace xe::kernel;
ExportResolver::ExportResolver() {
}
ExportResolver::~ExportResolver() {
}
void ExportResolver::RegisterTable(
const char* library_name, KernelExport* exports, const size_t count) {
ExportTable table;
XEIGNORE(xestrcpya(table.name, XECOUNT(table.name), library_name));
table.exports = exports;
table.count = count;
tables_.push_back(table);
for (size_t n = 0; n < count; n++) {
exports[n].is_implemented = false;
exports[n].variable_ptr = 0;
exports[n].function_data.shim_data = NULL;
exports[n].function_data.shim = NULL;
exports[n].function_data.impl = NULL;
}
}
KernelExport* ExportResolver::GetExportByOrdinal(const char* library_name,
const uint32_t ordinal) {
for (std::vector<ExportTable>::iterator it = tables_.begin();
it != tables_.end(); ++it) {
if (!xestrcmpa(library_name, it->name)) {
// TODO(benvanik): binary search?
for (size_t n = 0; n < it->count; n++) {
if (it->exports[n].ordinal == ordinal) {
return &it->exports[n];
}
}
return NULL;
}
}
return NULL;
}
KernelExport* ExportResolver::GetExportByName(const char* library_name,
const char* name) {
// TODO(benvanik): lookup by name.
XEASSERTALWAYS();
return NULL;
}
void ExportResolver::SetVariableMapping(const char* library_name,
const uint32_t ordinal,
uint32_t value) {
KernelExport* kernel_export = GetExportByOrdinal(library_name, ordinal);
XEASSERTNOTNULL(kernel_export);
kernel_export->is_implemented = true;
kernel_export->variable_ptr = value;
}
void ExportResolver::SetFunctionMapping(
const char* library_name, const uint32_t ordinal,
void* shim_data, xe_kernel_export_shim_fn shim,
xe_kernel_export_impl_fn impl) {
KernelExport* kernel_export = GetExportByOrdinal(library_name, ordinal);
XEASSERTNOTNULL(kernel_export);
kernel_export->is_implemented = true;
kernel_export->function_data.shim_data = shim_data;
kernel_export->function_data.shim = shim;
kernel_export->function_data.impl = impl;
}

108
src/xenia/kernel/export.h Normal file
View File

@@ -0,0 +1,108 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_EXPORT_H_
#define XENIA_KERNEL_EXPORT_H_
#include <xenia/core.h>
#include <vector>
typedef struct xe_ppc_state xe_ppc_state_t;
namespace xe {
namespace kernel {
typedef void (*xe_kernel_export_impl_fn)();
typedef void (*xe_kernel_export_shim_fn)(xe_ppc_state_t*, void*);
class KernelExport {
public:
enum ExportType {
Function = 0,
Variable = 1,
};
uint32_t ordinal;
ExportType type;
uint32_t flags;
char signature[16];
char name[96];
bool is_implemented;
union {
// Variable data. Only valid when kXEKernelExportFlagVariable is set.
// This is an address in the client memory space that the variable can
// be found at.
uint32_t variable_ptr;
struct {
// Second argument passed to the shim function.
void* shim_data;
// Shimmed implementation.
// This is called directly from generated code.
// It should parse args, do fixups, and call the impl.
xe_kernel_export_shim_fn shim;
// Real function implementation.
xe_kernel_export_impl_fn impl;
} function_data;
};
};
#define XE_DECLARE_EXPORT(module, ordinal, name, signature, type, flags) \
{ \
ordinal, \
KernelExport::type, \
flags, \
#signature, \
#name, \
}
class ExportResolver {
public:
ExportResolver();
~ExportResolver();
void RegisterTable(const char* library_name, KernelExport* exports,
const size_t count);
KernelExport* GetExportByOrdinal(const char* library_name,
const uint32_t ordinal);
KernelExport* GetExportByName(const char* library_name, const char* name);
void SetVariableMapping(const char* library_name, const uint32_t ordinal,
uint32_t value);
void SetFunctionMapping(const char* library_name, const uint32_t ordinal,
void* shim_data, xe_kernel_export_shim_fn shim,
xe_kernel_export_impl_fn impl);
private:
class ExportTable {
public:
char name[32];
KernelExport* exports;
size_t count;
};
std::vector<ExportTable> tables_;
};
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_EXPORT_H_

View File

@@ -0,0 +1,34 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/kernel/fs/device.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::fs;
Device::Device(xe_pal_ref pal, const char* path) {
pal_ = xe_pal_retain(pal);
path_ = xestrdupa(path);
}
Device::~Device() {
xe_free(path_);
xe_pal_release(pal_);
}
xe_pal_ref Device::pal() {
return xe_pal_retain(pal_);
}
const char* Device::path() {
return path_;
}

View File

@@ -0,0 +1,45 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_FS_DEVICE_H_
#define XENIA_KERNEL_FS_DEVICE_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/kernel/fs/entry.h>
namespace xe {
namespace kernel {
namespace fs {
class Device {
public:
Device(xe_pal_ref pal, const char* path);
virtual ~Device();
xe_pal_ref pal();
const char* path();
virtual Entry* ResolvePath(const char* path) = 0;
protected:
xe_pal_ref pal_;
char* path_;
};
} // namespace fs
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_FS_DEVICE_H_

View File

@@ -0,0 +1,157 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/kernel/fs/devices/disc_image_device.h>
#include <xenia/kernel/fs/gdfx.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::fs;
namespace {
class DiscImageMemoryMapping : public MemoryMapping {
public:
DiscImageMemoryMapping(uint8_t* address, size_t length, xe_mmap_ref mmap) :
MemoryMapping(address, length) {
mmap_ = xe_mmap_retain(mmap);
}
virtual ~DiscImageMemoryMapping() {
xe_mmap_release(mmap_);
}
private:
xe_mmap_ref mmap_;
};
class DiscImageFileEntry : public FileEntry {
public:
DiscImageFileEntry(Device* device, const char* path,
xe_mmap_ref mmap, GDFXEntry* gdfx_entry) :
FileEntry(device, path),
gdfx_entry_(gdfx_entry) {
mmap_ = xe_mmap_retain(mmap);
}
virtual ~DiscImageFileEntry() {
xe_mmap_release(mmap_);
}
virtual MemoryMapping* CreateMemoryMapping(
xe_file_mode file_mode, const size_t offset, const size_t length) {
if (file_mode & kXEFileModeWrite) {
// Only allow reads.
return NULL;
}
size_t real_offset = gdfx_entry_->offset + offset;
size_t real_length = length ?
MIN(length, gdfx_entry_->size) : gdfx_entry_->size;
return new DiscImageMemoryMapping(
xe_mmap_get_addr(mmap_) + real_offset,
real_length,
mmap_);
}
private:
xe_mmap_ref mmap_;
GDFXEntry* gdfx_entry_;
};
}
DiscImageDevice::DiscImageDevice(xe_pal_ref pal, const char* path,
const xechar_t* local_path) :
Device(pal, path) {
local_path_ = xestrdup(local_path);
mmap_ = NULL;
gdfx_ = NULL;
}
DiscImageDevice::~DiscImageDevice() {
delete gdfx_;
xe_mmap_release(mmap_);
xe_free(local_path_);
}
int DiscImageDevice::Init() {
mmap_ = xe_mmap_open(pal_, kXEFileModeRead, local_path_, 0, 0);
if (!mmap_) {
XELOGE(XT("Disc image could not be mapped"));
return 1;
}
gdfx_ = new GDFX(mmap_);
GDFX::Error error = gdfx_->Load();
if (error != GDFX::kSuccess) {
XELOGE(XT("GDFX init failed: %d"), error);
return 1;
}
//gdfx_->Dump();
return 0;
}
Entry* DiscImageDevice::ResolvePath(const char* path) {
// The filesystem will have stripped our prefix off already, so the path will
// be in the form:
// some\PATH.foo
XELOGFS(XT("DiscImageDevice::ResolvePath(%s)"), path);
GDFXEntry* gdfx_entry = gdfx_->root_entry();
// Walk the path, one separator at a time.
// We copy it into the buffer and shift it left over and over.
char remaining[XE_MAX_PATH];
XEIGNORE(xestrcpya(remaining, XECOUNT(remaining), path));
while (remaining[0]) {
char* next_slash = xestrchra(remaining, '\\');
if (next_slash == remaining) {
// Leading slash - shift
XEIGNORE(xestrcpya(remaining, XECOUNT(remaining), remaining + 1));
continue;
}
// Make the buffer just the name.
if (next_slash) {
*next_slash = 0;
}
// Look up in the entry.
gdfx_entry = gdfx_entry->GetChild(remaining);
if (!gdfx_entry) {
// Not found.
return NULL;
}
// Shift the buffer down, unless we are at the end.
if (!next_slash) {
break;
}
XEIGNORE(xestrcpya(remaining, XECOUNT(remaining), next_slash + 1));
}
if (gdfx_entry->attributes & GDFXEntry::kAttrFolder) {
//return new DiscImageDirectoryEntry(mmap_, gdfx_entry);
XEASSERTALWAYS();
return NULL;
} else {
return new DiscImageFileEntry(this, path, mmap_, gdfx_entry);
}
}

View File

@@ -0,0 +1,48 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
#define XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/kernel/fs/device.h>
namespace xe {
namespace kernel {
namespace fs {
class GDFX;
class DiscImageDevice : public Device {
public:
DiscImageDevice(xe_pal_ref pal, const char* path, const xechar_t* local_path);
virtual ~DiscImageDevice();
int Init();
virtual Entry* ResolvePath(const char* path);
private:
xechar_t* local_path_;
xe_mmap_ref mmap_;
GDFX* gdfx_;
};
} // namespace fs
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_FS_DEVICES_DISC_IMAGE_DEVICE_H_

View File

@@ -0,0 +1,112 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/kernel/fs/devices/local_directory_device.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::fs;
namespace {
class LocalFileMemoryMapping : public MemoryMapping {
public:
LocalFileMemoryMapping(uint8_t* address, size_t length, xe_mmap_ref mmap) :
MemoryMapping(address, length) {
mmap_ = xe_mmap_retain(mmap);
}
virtual ~LocalFileMemoryMapping() {
xe_mmap_release(mmap_);
}
private:
xe_mmap_ref mmap_;
};
class LocalFileEntry : public FileEntry {
public:
LocalFileEntry(Device* device, const char* path, const xechar_t* local_path) :
FileEntry(device, path) {
local_path_ = xestrdup(local_path);
}
virtual ~LocalFileEntry() {
xe_free(local_path_);
}
const xechar_t* local_path() { return local_path_; }
virtual MemoryMapping* CreateMemoryMapping(
xe_file_mode file_mode, const size_t offset, const size_t length) {
xe_pal_ref pal = device()->pal();
xe_mmap_ref mmap = xe_mmap_open(pal, file_mode, local_path_,
offset, length);
xe_pal_release(pal);
if (!mmap) {
return NULL;
}
LocalFileMemoryMapping* lfmm = new LocalFileMemoryMapping(
(uint8_t*)xe_mmap_get_addr(mmap), xe_mmap_get_length(mmap),
mmap);
xe_mmap_release(mmap);
return lfmm;
}
private:
xechar_t* local_path_;
};
}
LocalDirectoryDevice::LocalDirectoryDevice(xe_pal_ref pal, const char* path,
const xechar_t* local_path) :
Device(pal, path) {
local_path_ = xestrdup(local_path);
}
LocalDirectoryDevice::~LocalDirectoryDevice() {
xe_free(local_path_);
}
Entry* LocalDirectoryDevice::ResolvePath(const char* path) {
// The filesystem will have stripped our prefix off already, so the path will
// be in the form:
// some\PATH.foo
XELOGFS(XT("LocalDirectoryDevice::ResolvePath(%s)"), path);
xechar_t full_path[XE_MAX_PATH];
xesnprintf(full_path, XECOUNT(full_path), XT("%s%c%s"),
local_path_, XE_PATH_SEPARATOR, path);
// Swap around path separators.
if (XE_PATH_SEPARATOR != '\\') {
for (size_t n = 0; n < XECOUNT(full_path); n++) {
if (full_path[n] == 0) {
break;
}
if (full_path[n] == '\\') {
full_path[n] = XE_PATH_SEPARATOR;
}
}
}
// TODO(benvanik): get file info
// TODO(benvanik): fail if does not exit
// TODO(benvanik): switch based on type
LocalFileEntry* file_entry = new LocalFileEntry(this, path, full_path);
return file_entry;
}

View File

@@ -0,0 +1,42 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_FS_DEVICES_LOCAL_DIRECTORY_DEVICE_H_
#define XENIA_KERNEL_FS_DEVICES_LOCAL_DIRECTORY_DEVICE_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/kernel/fs/device.h>
namespace xe {
namespace kernel {
namespace fs {
class LocalDirectoryDevice : public Device {
public:
LocalDirectoryDevice(xe_pal_ref pal, const char* path,
const xechar_t* local_path);
virtual ~LocalDirectoryDevice();
virtual Entry* ResolvePath(const char* path);
private:
xechar_t* local_path_;
};
} // namespace fs
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_FS_DEVICES_LOCAL_DIRECTORY_DEVICE_H_

View File

@@ -0,0 +1,9 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'disc_image_device.cc',
'disc_image_device.h',
'local_directory_device.cc',
'local_directory_device.h',
],
}

View File

@@ -0,0 +1,77 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/kernel/fs/entry.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::fs;
Entry::Entry(Type type, Device* device, const char* path) :
type_(type),
device_(device) {
path_ = xestrdupa(path);
// TODO(benvanik): last index of \, unless \ at end, then before that
name_ = NULL;
}
Entry::~Entry() {
xe_free(path_);
xe_free(name_);
}
Entry::Type Entry::type() {
return type_;
}
Device* Entry::device() {
return device_;
}
const char* Entry::path() {
return path_;
}
const char* Entry::name() {
return name_;
}
MemoryMapping::MemoryMapping(uint8_t* address, size_t length) :
address_(address), length_(length) {
}
MemoryMapping::~MemoryMapping() {
}
uint8_t* MemoryMapping::address() {
return address_;
}
size_t MemoryMapping::length() {
return length_;
}
FileEntry::FileEntry(Device* device, const char* path) :
Entry(kTypeFile, device, path) {
}
FileEntry::~FileEntry() {
}
DirectoryEntry::DirectoryEntry(Device* device, const char* path) :
Entry(kTypeDirectory, device, path) {
}
DirectoryEntry::~DirectoryEntry() {
}

View File

@@ -0,0 +1,88 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_FS_ENTRY_H_
#define XENIA_KERNEL_FS_ENTRY_H_
#include <xenia/common.h>
#include <xenia/core.h>
namespace xe {
namespace kernel {
namespace fs {
class Device;
class Entry {
public:
enum Type {
kTypeFile,
kTypeDirectory,
};
Entry(Type type, Device* device, const char* path);
virtual ~Entry();
Type type();
Device* device();
const char* path();
const char* name();
private:
Type type_;
Device* device_;
char* path_;
char* name_;
};
class MemoryMapping {
public:
MemoryMapping(uint8_t* address, size_t length);
virtual ~MemoryMapping();
uint8_t* address();
size_t length();
private:
uint8_t* address_;
size_t length_;
};
class FileEntry : public Entry {
public:
FileEntry(Device* device, const char* path);
virtual ~FileEntry();
//virtual void Query() = 0;
virtual MemoryMapping* CreateMemoryMapping(
xe_file_mode file_mode, const size_t offset, const size_t length) = 0;
};
class DirectoryEntry : public Entry {
public:
DirectoryEntry(Device* device, const char* path);
virtual ~DirectoryEntry();
//virtual void Query() = 0;
};
} // namespace fs
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_FS_ENTRY_H_

View File

@@ -0,0 +1,116 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/kernel/fs/filesystem.h>
#include <xenia/kernel/fs/devices/disc_image_device.h>
#include <xenia/kernel/fs/devices/local_directory_device.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::fs;
FileSystem::FileSystem(xe_pal_ref pal) {
pal_ = xe_pal_retain(pal);
}
FileSystem::~FileSystem() {
// Delete all devices.
// This will explode if anyone is still using data from them.
for (std::vector<Device*>::iterator it = devices_.begin();
it != devices_.end(); ++it) {
delete *it;
}
devices_.clear();
symlinks_.clear();
xe_pal_release(pal_);
}
int FileSystem::RegisterDevice(const char* path, Device* device) {
devices_.push_back(device);
return 0;
}
int FileSystem::RegisterLocalDirectoryDevice(
const char* path, const xechar_t* local_path) {
Device* device = new LocalDirectoryDevice(pal_, path, local_path);
return RegisterDevice(path, device);
}
int FileSystem::RegisterDiscImageDevice(
const char* path, const xechar_t* local_path) {
DiscImageDevice* device = new DiscImageDevice(pal_, path, local_path);
if (device->Init()) {
return 1;
}
return RegisterDevice(path, device);
}
int FileSystem::CreateSymbolicLink(const char* path, const char* target) {
symlinks_.insert(std::pair<const char*, const char*>(
xestrdupa(path),
xestrdupa(target)));
return 0;
}
int FileSystem::DeleteSymbolicLink(const char* path) {
std::tr1::unordered_map<std::string, std::string>::iterator it =
symlinks_.find(std::string(path));
if (it != symlinks_.end()) {
symlinks_.erase(it);
return 0;
}
return 1;
}
Entry* FileSystem::ResolvePath(const char* path) {
// Strip off prefix and pass to device.
// e.g., d:\some\PATH.foo -> some\PATH.foo
// Support both symlinks and device specifiers, like:
// \\Device\Foo\some\PATH.foo, d:\some\PATH.foo, etc.
// TODO(benvanik): normalize path/etc
// e.g., remove ..'s and such
// Resolve symlinks.
// TODO(benvanik): more robust symlink handling - right now we assume simple
// drive path -> device mappings with nothing nested.
char full_path[XE_MAX_PATH];
XEIGNORE(xestrcpya(full_path, XECOUNT(full_path), path));
for (std::tr1::unordered_map<std::string, std::string>::iterator it =
symlinks_.begin(); it != symlinks_.end(); ++it) {
if (xestrcasestra(path, it->first.c_str()) == path) {
// Found symlink, fixup.
const char* after_path = path + it->first.size();
XEIGNORE(xesnprintf(full_path, XECOUNT(full_path), "%s%s",
it->second.c_str(), after_path));
break;
}
}
// Scan all devices.
for (std::vector<Device*>::iterator it = devices_.begin();
it != devices_.end(); ++it) {
Device* device = *it;
if (xestrcasestra(full_path, device->path()) == full_path) {
// Found!
// Trim the device prefix off and pass down.
char device_path[XE_MAX_PATH];
XEIGNORE(xestrcpya(device_path, XECOUNT(device_path),
full_path + xestrlena(device->path())));
return device->ResolvePath(device_path);
}
}
XELOGE(XT("ResolvePath(%s) failed - no root found"), path);
return NULL;
}

View File

@@ -0,0 +1,56 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_FS_FILESYSTEM_H_
#define XENIA_KERNEL_FS_FILESYSTEM_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <vector>
#include <xenia/kernel/fs/entry.h>
namespace xe {
namespace kernel {
namespace fs {
class Device;
class FileSystem {
public:
FileSystem(xe_pal_ref pal);
~FileSystem();
int RegisterDevice(const char* path, Device* device);
int RegisterLocalDirectoryDevice(const char* path,
const xechar_t* local_path);
int RegisterDiscImageDevice(const char* path, const xechar_t* local_path);
int CreateSymbolicLink(const char* path, const char* target);
int DeleteSymbolicLink(const char* path);
Entry* ResolvePath(const char* path);
private:
xe_pal_ref pal_;
std::vector<Device*> devices_;
std::tr1::unordered_map<std::string, std::string> symlinks_;
};
} // namespace fs
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_FS_FILESYSTEM_H_

209
src/xenia/kernel/fs/gdfx.cc Normal file
View File

@@ -0,0 +1,209 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
* Major contributions to this file from:
* - abgx360
*/
#include <xenia/kernel/fs/gdfx.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::fs;
namespace {
#define kXESectorSize 2048
}
GDFXEntry::GDFXEntry() :
attributes(0), offset(0), size(0) {
}
GDFXEntry::~GDFXEntry() {
for (std::vector<GDFXEntry*>::iterator it = children.begin();
it != children.end(); ++it) {
delete *it;
}
}
GDFXEntry* GDFXEntry::GetChild(const char* name) {
// TODO(benvanik): a faster search
for (std::vector<GDFXEntry*>::iterator it = children.begin();
it != children.end(); ++it) {
GDFXEntry* entry = *it;
if (xestrcasecmpa(entry->name.c_str(), name) == 0) {
return entry;
}
}
return NULL;
}
void GDFXEntry::Dump(int indent) {
printf("%s%s\n", std::string(indent, ' ').c_str(), name.c_str());
for (std::vector<GDFXEntry*>::iterator it = children.begin();
it != children.end(); ++it) {
GDFXEntry* entry = *it;
entry->Dump(indent + 2);
}
}
GDFX::GDFX(xe_mmap_ref mmap) {
mmap_ = xe_mmap_retain(mmap);
root_entry_ = NULL;
}
GDFX::~GDFX() {
delete root_entry_;
xe_mmap_release(mmap_);
}
GDFXEntry* GDFX::root_entry() {
return root_entry_;
}
GDFX::Error GDFX::Load() {
Error result = kErrorOutOfMemory;
ParseState state;
xe_zero_struct(&state, sizeof(state));
state.ptr = (uint8_t*)xe_mmap_get_addr(mmap_);
state.size = xe_mmap_get_length(mmap_);
result = Verify(state);
XEEXPECTZERO(result);
result = ReadAllEntries(state, state.ptr + state.root_offset);
XEEXPECTZERO(result);
result = kSuccess;
XECLEANUP:
return result;
}
void GDFX::Dump() {
if (root_entry_) {
root_entry_->Dump(0);
}
}
GDFX::Error GDFX::Verify(ParseState& state) {
// Find sector 32 of the game partition - try at a few points.
const static size_t likely_offsets[] = {
0x00000000, 0x0000FB20, 0x00020600, 0x0FD90000,
};
bool magic_found = false;
for (size_t n = 0; n < XECOUNT(likely_offsets); n++) {
state.game_offset = likely_offsets[n];
if (VerifyMagic(state, state.game_offset + (32 * kXESectorSize))) {
magic_found = true;
break;
}
}
if (!magic_found) {
// File doesn't have the magic values - likely not a real GDFX source.
return kErrorFileMismatch;
}
// Read sector 32 to get FS state.
if (state.size < state.game_offset + (32 * kXESectorSize)) {
return kErrorReadError;
}
uint8_t* fs_ptr = state.ptr + state.game_offset + (32 * kXESectorSize);
state.root_sector = XEGETUINT32LE(fs_ptr + 20);
state.root_size = XEGETUINT32LE(fs_ptr + 24);
state.root_offset = state.game_offset + (state.root_sector * kXESectorSize);
if (state.root_size < 13 ||
state.root_size > 32 * 1024 * 1024) {
return kErrorDamagedFile;
}
return kSuccess;
}
bool GDFX::VerifyMagic(ParseState& state, size_t offset) {
// Simple check to see if the given offset contains the magic value.
return memcmp(state.ptr + offset, "MICROSOFT*XBOX*MEDIA", 20) == 0;
}
GDFX::Error GDFX::ReadAllEntries(ParseState& state,
const uint8_t* root_buffer) {
root_entry_ = new GDFXEntry();
root_entry_->offset = 0;
root_entry_->size = 0;
root_entry_->name = "";
root_entry_->attributes = GDFXEntry::kAttrFolder;
if (!ReadEntry(state, root_buffer, 0, root_entry_)) {
return kErrorOutOfMemory;
}
return kSuccess;
}
bool GDFX::ReadEntry(ParseState& state, const uint8_t* buffer,
uint16_t entry_ordinal, GDFXEntry* parent) {
const uint8_t* p = buffer + (entry_ordinal * 4);
uint16_t node_l = XEGETUINT16LE(p + 0);
uint16_t node_r = XEGETUINT16LE(p + 2);
size_t sector = XEGETUINT32LE(p + 4);
size_t length = XEGETUINT32LE(p + 8);
uint8_t attributes = XEGETUINT8LE(p + 12);
uint8_t name_length = XEGETUINT8LE(p + 13);
char* name = (char*)(p + 14);
if (node_l && !ReadEntry(state, buffer, node_l, parent)) {
return false;
}
GDFXEntry* entry = new GDFXEntry();
entry->name = std::string(name, name_length);
entry->name.append(1, '\0');
entry->attributes = attributes;
// Add to parent.
parent->children.push_back(entry);
if (attributes & GDFXEntry::kAttrFolder) {
// Folder.
entry->offset = 0;
entry->size = 0;
if (length) {
// Not a leaf - read in children.
if (state.size < state.game_offset + (sector * kXESectorSize)) {
// Out of bounds read.
return false;
}
// Read child list.
uint8_t* folder_ptr =
state.ptr + state.game_offset + (sector * kXESectorSize);
if (!ReadEntry(state, folder_ptr, 0, entry)) {
return false;
}
}
} else {
// File.
entry->offset = state.game_offset + (sector * kXESectorSize);
entry->size = length;
}
// Read next file in the list.
if (node_r && !ReadEntry(state, buffer, node_r, parent)) {
return false;
}
return true;
}

105
src/xenia/kernel/fs/gdfx.h Normal file
View File

@@ -0,0 +1,105 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_FS_GDFX_H_
#define XENIA_KERNEL_FS_GDFX_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <vector>
#include <xenia/kernel/fs/entry.h>
namespace xe {
namespace kernel {
namespace fs {
class GDFX;
class GDFXEntry {
public:
enum Attributes {
kAttrNone = 0x00000000,
kAttrReadOnly = 0x00000001,
kAttrHidden = 0x00000002,
kAttrSystem = 0x00000004,
kAttrFolder = 0x00000010,
kAttrArchived = 0x00000020,
kAttrNormal = 0x00000080,
};
GDFXEntry();
~GDFXEntry();
GDFXEntry* GetChild(const char* name);
void Dump(int indent);
std::string name;
uint32_t attributes;
size_t offset;
size_t size;
std::vector<GDFXEntry*> children;
};
class GDFX {
public:
enum Error {
kSuccess = 0,
kErrorOutOfMemory = -1,
kErrorReadError = -10,
kErrorFileMismatch = -30,
kErrorDamagedFile = -31,
};
GDFX(xe_mmap_ref mmap);
virtual ~GDFX();
GDFXEntry* root_entry();
Error Load();
void Dump();
private:
typedef struct {
uint8_t* ptr;
size_t size; // Size (bytes) of total image
size_t game_offset; // Offset (bytes) of game partition
size_t root_sector; // Offset (sector) of root
size_t root_offset; // Offset (bytes) of root
size_t root_size; // Size (bytes) of root
} ParseState;
Error Verify(ParseState& state);
bool VerifyMagic(ParseState& state, size_t offset);
Error ReadAllEntries(ParseState& state, const uint8_t* root_buffer);
bool ReadEntry(ParseState& state, const uint8_t* buffer,
uint16_t entry_ordinal, GDFXEntry* parent);
xe_mmap_ref mmap_;
GDFXEntry* root_entry_;
};
} // namespace fs
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_FS_GDFX_H_

View File

@@ -0,0 +1,17 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'device.cc',
'device.h',
'entry.cc',
'entry.h',
'filesystem.cc',
'filesystem.h',
'gdfx.cc',
'gdfx.h',
],
'includes': [
'devices/sources.gypi',
],
}

15
src/xenia/kernel/kernel.h Normal file
View File

@@ -0,0 +1,15 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_KERNEL_H_
#define XENIA_KERNEL_KERNEL_H_
#include <xenia/kernel/runtime.h>
#endif // XENIA_KERNEL_KERNEL_H_

View File

@@ -0,0 +1,54 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_KERNEL_MODULE_H_
#define XENIA_KERNEL_KERNEL_MODULE_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/runtime.h>
namespace xe {
namespace kernel {
class Runtime;
class KernelModule {
public:
KernelModule(Runtime* runtime) {
runtime_ = runtime;
pal_ = runtime->pal();
memory_ = runtime->memory();
export_resolver_ = runtime->export_resolver();
}
virtual ~KernelModule() {
export_resolver_.reset();
xe_memory_release(memory_);
xe_pal_release(pal_);
}
protected:
Runtime* runtime_;
xe_pal_ref pal_;
xe_memory_ref memory_;
shared_ptr<ExportResolver> export_resolver_;
};
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_KERNEL_MODULE_H_

View File

@@ -0,0 +1,16 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_MODULES_H_
#define XENIA_KERNEL_MODULES_H_
#include <xenia/kernel/modules/xam/xam_module.h>
#include <xenia/kernel/modules/xboxkrnl/module.h>
#endif // XENIA_KERNEL_MODULES_H_

View File

@@ -0,0 +1,11 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'modules.h',
],
'includes': [
'xam/sources.gypi',
'xboxkrnl/sources.gypi',
],
}

Some files were not shown because too many files have changed in this diff Show More