Merge branch 'master' into qt

This commit is contained in:
Jeffrey Pfau 2014-10-12 21:00:01 -07:00
commit 51ad9d37e1
54 changed files with 361 additions and 131 deletions

View File

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 2.6)
project(mGBA)
project(mGBA C)
set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries")
set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -std=gnu99")
set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=gnu99")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -Wall -Wextra -std=gnu99")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wall -Wextra -std=gnu99")
set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger")
set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger")
set(USE_FFMPEG ON CACHE BOOL "Whether or not to enable FFmpeg support")
@ -24,9 +24,14 @@ include_directories(${CMAKE_SOURCE_DIR}/src/arm)
include_directories(${CMAKE_SOURCE_DIR}/src/gba)
include_directories(${CMAKE_SOURCE_DIR}/src)
include(GNUInstallDirs)
# Function definitions
include(FindPkgConfig)
function(find_feature FEATURE_NAME FEATURE_REQUIRES)
if (NOT ${FEATURE_NAME})
return()
endif()
foreach(REQUIRE ${FEATURE_REQUIRES})
find_package(${REQUIRE} QUIET)
pkg_search_module(${REQUIRE} ${REQUIRE})
@ -35,6 +40,13 @@ function(find_feature FEATURE_NAME FEATURE_REQUIRES)
set(${FEATURE_NAME} OFF PARENT_SCOPE)
return()
endif()
string(TOUPPER ${REQUIRE} UREQUIRE)
set(${UREQUIRE}_CFLAGS_OTHER ${${REQUIRE}_CFLAGS_OTHER} PARENT_SCOPE)
set(${UREQUIRE}_FOUND ${${REQUIRE}_FOUND} PARENT_SCOPE)
set(${UREQUIRE}_INCLUDE_DIRS ${${REQUIRE}_INCLUDE_DIRS} PARENT_SCOPE)
set(${UREQUIRE}_LIBRARIES ${${REQUIRE}_LIBRARIES} PARENT_SCOPE)
set(${UREQUIRE}_LIBRARY_DIRS ${${REQUIRE}_LIBRARY_DIRS} PARENT_SCOPE)
set(${UREQUIRE}_LDFLAGS_OTHER ${${REQUIRE}_LDFLAGS_OTHER} PARENT_SCOPE)
endforeach()
endfunction()
@ -98,7 +110,9 @@ if(USE_CLI_DEBUGGER)
add_definitions(-DUSE_CLI_DEBUGGER)
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c)
list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c)
set(DEBUGGER_LIB ${EDIT_LIBRARIES})
include_directories(AFTER ${LIBEDIT_INCLUDE_DIRS})
link_directories(${LIBEDIT_LIBRARY_DIRS})
set(DEBUGGER_LIB ${LIBEDIT_LIBRARIES})
else()
set(DEBUGGER_LIB "")
endif()
@ -110,24 +124,22 @@ endif()
source_group("ARM debugger" FILES ${DEBUGGER_SRC})
if(USE_FFMPEG)
pkg_search_module(LIBAVCODEC libavcodec)
pkg_search_module(LIBAVFORMAT libavcodec;libavformat;libavutil)
pkg_search_module(LIBAVUTIL libavutil)
add_definitions(-DUSE_FFMPEG)
include_directories(AFTER ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS})
link_directories(${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS})
list(APPEND UTIL_SRC "${CMAKE_SOURCE_DIR}/src/platform/ffmpeg/ffmpeg-encoder.c")
list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVUTIL_LIBRARIES})
endif()
if(USE_PNG)
find_package(PNG)
find_package(ZLIB)
add_definitions(-DUSE_PNG)
include_directories(${PNG_PNG_INCLUDE_DIR})
include_directories(AFTER ${PNG_INCLUDE_DIRS})
list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
endif()
if(USE_LIBZIP)
include_directories(${LIBZIP_INCLUDE_DIRS})
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
link_directories(${LIBZIP_LIBRARY_DIRS})
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
add_definitions(-DENABLE_LIBZIP)
endif()
@ -156,6 +168,7 @@ if(BUILD_PERF)
add_executable(${BINARY_NAME}-perf ${PERF_SRC})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB})
install(TARGETS ${BINARY_NAME}-perf DESTINATION bin)
install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}")
endif()
# Summaries

View File

@ -1,7 +1,7 @@
#ifndef ARM_H
#define ARM_H
#include "common.h"
#include "util/common.h"
enum {
ARM_SP = 13,

View File

@ -1,7 +1,7 @@
#ifndef ISA_ARM_H
#define ISA_ARM_H
#include "common.h"
#include "util/common.h"
#define ARM_PREFETCH_CYCLES (1 + cpu->memory.activeSeqCycles32)

View File

@ -1,7 +1,7 @@
#ifndef ISA_INLINES_H
#define ISA_INLINES_H
#include "common.h"
#include "macros.h"
#include "arm.h"

View File

@ -1,7 +1,7 @@
#ifndef ISA_THUMB_H
#define ISA_THUMB_H
#include "common.h"
#include "util/common.h"
struct ARMCore;

View File

@ -1,20 +1,7 @@
#ifndef COMMON_H
#define COMMON_H
#ifndef MACROS_H
#define MACROS_H
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define UNUSED(V) (void)(V)
#include "util/common.h"
#if defined(__PPC__) || defined(__POWERPC__)
#define LOAD_32(DEST, ADDR, ARR) { \

View File

@ -72,6 +72,9 @@ static struct {
{ "disasm", _disassemble, _DVParse, "Disassemble instructions" },
{ "disasm/a", _disassembleArm, _DVParse, "Disassemble instructions as ARM" },
{ "disasm/t", _disassembleThumb, _DVParse, "Disassemble instructions as Thumb" },
{ "disassemble", _disassemble, _DVParse, "Disassemble instructions" },
{ "disassemble/a", _disassembleArm, _DVParse, "Disassemble instructions as ARM" },
{ "disassemble/t", _disassembleThumb, _DVParse, "Disassemble instructions as Thumb" },
{ "h", _printHelp, _DVStringParse, "Print help" },
{ "help", _printHelp, _DVStringParse, "Print help" },
{ "i", _printStatus, 0, "Print the current status" },
@ -233,16 +236,17 @@ static void _printHelp(struct CLIDebugger* debugger, struct DebugVector* dv) {
static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
char disassembly[48];
struct ARMInstructionInfo info;
printf("%08X: ", address);
if (mode == MODE_ARM) {
uint32_t instruction = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
ARMDecodeARM(instruction, &info);
ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
printf("%08X: %s\n", instruction, disassembly);
printf("%08X\t%s\n", instruction, disassembly);
} else {
uint16_t instruction = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
ARMDecodeThumb(instruction, &info);
ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
printf("%04X: %s\n", instruction, disassembly);
printf("%04X\t%s\n", instruction, disassembly);
}
}
@ -579,6 +583,10 @@ static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason
static unsigned char _tabComplete(EditLine* elstate, int ch) {
UNUSED(ch);
const LineInfo* li = el_line(elstate);
if (!li->buffer[0]) {
return CC_ERROR;
}
const char* commandPtr;
int cmd = 0, len = 0;
const char* name = 0;
@ -593,9 +601,29 @@ static unsigned char _tabComplete(EditLine* elstate, int ch) {
}
}
}
if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
if (!name) {
return CC_ERROR;
}
if (_debuggerCommands[cmd + 1].name && name[len - 2] == _debuggerCommands[cmd + 1].name[len - 2]) {
--len;
const char* next = 0;
int i;
for (i = cmd + 1; _debuggerCommands[i].name; ++i) {
if (strncasecmp(name, _debuggerCommands[i].name, len)) {
break;
}
next = _debuggerCommands[i].name;
}
for (; name[len]; ++len) {
if (name[len] != next[len]) {
break;
}
char out[2] = { name[len], '\0' };
el_insertstr(elstate, out);
}
return CC_REDISPLAY;
}
name += len - 1;
el_insertstr(elstate, name);
el_insertstr(elstate, " ");

View File

@ -1,7 +1,7 @@
#ifndef CLI_DEBUGGER_H
#define CLI_DEBUGGER_H
#include "common.h"
#include "util/common.h"
#include "debugger.h"

View File

@ -1,7 +1,7 @@
#ifndef DEBUGGER_H
#define DEBUGGER_H
#include "common.h"
#include "util/common.h"
#include "arm.h"

View File

@ -1,7 +1,7 @@
#ifndef GDB_STUB_H
#define GDB_STUB_H
#include "common.h"
#include "util/common.h"
#include "debugger/debugger.h"

View File

@ -1,7 +1,7 @@
#ifndef MEMORY_DEBUGGER_H
#define MEMORY_DEBUGGER_H
#include "common.h"
#include "util/common.h"
#include "arm.h"

View File

@ -77,6 +77,7 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
break;
case '0':
state = LEX_EXPECT_PREFIX;
next = 0;
break;
case '$':
state = LEX_EXPECT_HEX;
@ -120,6 +121,33 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
break;
}
break;
case LEX_EXPECT_BINARY:
switch (token) {
case '0':
case '1':
// TODO: handle overflow
next <<= 1;
next += token - '0';
break;
case '+':
case '-':
case '*':
case '/':
lv->token.type = TOKEN_UINT_TYPE;
lv->token.uintValue = next;
lv = _lexOperator(lv, token);
state = LEX_ROOT;
break;
case ')':
lv->token.type = TOKEN_UINT_TYPE;
lv->token.uintValue = next;
state = LEX_EXPECT_OPERATOR;
break;
default:
state = LEX_ERROR;
break;
}
break;
case LEX_EXPECT_DECIMAL:
switch (token) {
case '0':
@ -216,8 +244,24 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
next = 0;
state = LEX_EXPECT_HEX;
break;
default:
state = LEX_ERROR;
case 'B':
case 'b':
next = 0;
state = LEX_EXPECT_BINARY;
break;
case '+':
case '-':
case '*':
case '/':
lv->token.type = TOKEN_UINT_TYPE;
lv->token.uintValue = next;
lv = _lexOperator(lv, token);
state = LEX_ROOT;
break;
case ')':
lv->token.type = TOKEN_UINT_TYPE;
lv->token.uintValue = next;
state = LEX_EXPECT_OPERATOR;
break;
}
break;
@ -245,8 +289,10 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
}
switch (state) {
case LEX_EXPECT_BINARY:
case LEX_EXPECT_DECIMAL:
case LEX_EXPECT_HEX:
case LEX_EXPECT_PREFIX:
lv->token.type = TOKEN_UINT_TYPE;
lv->token.uintValue = next;
break;

View File

@ -1,13 +1,14 @@
#ifndef PARSER_H
#define PARSER_H
#include "common.h"
#include "util/common.h"
#include "debugger.h"
enum LexState {
LEX_ERROR = -1,
LEX_ROOT = 0,
LEX_EXPECT_IDENTIFIER,
LEX_EXPECT_BINARY,
LEX_EXPECT_DECIMAL,
LEX_EXPECT_HEX,
LEX_EXPECT_PREFIX,

View File

@ -1,7 +1,8 @@
#ifndef GBA_AUDIO_H
#define GBA_AUDIO_H
#include "common.h"
#include "util/common.h"
#include "macros.h"
#include "util/circle-buffer.h"

View File

@ -283,6 +283,11 @@ static void _unLz77(struct GBA* gba, uint32_t source, uint8_t* dest) {
}
}
DECL_BITFIELD(HuffmanNode, uint8_t);
DECL_BITS(HuffmanNode, Offset, 0, 6);
DECL_BIT(HuffmanNode, RTerm, 6);
DECL_BIT(HuffmanNode, LTerm, 7);
static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t* dest) {
struct ARMCore* cpu = gba->cpu;
source = source & 0xFFFFFFFC;
@ -303,39 +308,32 @@ static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t* dest) {
uint32_t sPointer = source + 5 + treesize;
uint32_t* dPointer = dest;
uint32_t nPointer = treeBase;
union HuffmanNode {
struct {
unsigned offset : 6;
unsigned rTerm : 1;
unsigned lTerm : 1;
};
uint8_t packed;
} node;
HuffmanNode node;
int bitsRemaining;
int readBits;
int bitsSeen = 0;
node.packed = cpu->memory.load8(cpu, nPointer, 0);
node = cpu->memory.load8(cpu, nPointer, 0);
while (remaining > 0) {
uint32_t bitstream = cpu->memory.load32(cpu, sPointer, 0);
sPointer += 4;
for (bitsRemaining = 32; bitsRemaining > 0; --bitsRemaining, bitstream <<= 1) {
uint32_t next = (nPointer & ~1) + node.offset * 2 + 2;
uint32_t next = (nPointer & ~1) + HuffmanNodeGetOffset(node) * 2 + 2;
if (bitstream & 0x80000000) {
// Go right
if (node.rTerm) {
if (HuffmanNodeIsRTerm(node)) {
readBits = cpu->memory.load8(cpu, next + 1, 0);
} else {
nPointer = next + 1;
node.packed = cpu->memory.load8(cpu, nPointer, 0);
node = cpu->memory.load8(cpu, nPointer, 0);
continue;
}
} else {
// Go left
if (node.lTerm) {
if (HuffmanNodeIsLTerm(node)) {
readBits = cpu->memory.load8(cpu, next, 0);
} else {
nPointer = next;
node.packed = cpu->memory.load8(cpu, nPointer, 0);
node = cpu->memory.load8(cpu, nPointer, 0);
continue;
}
}
@ -343,7 +341,7 @@ static void _unHuffman(struct GBA* gba, uint32_t source, uint32_t* dest) {
block |= (readBits & ((1 << bits) - 1)) << bitsSeen;
bitsSeen += bits;
nPointer = treeBase;
node.packed = cpu->memory.load8(cpu, nPointer, 0);
node = cpu->memory.load8(cpu, nPointer, 0);
if (bitsSeen == 32) {
bitsSeen = 0;
*dPointer = block;

View File

@ -1,7 +1,7 @@
#ifndef GBA_BIOS_H
#define GBA_BIOS_H
#include "common.h"
#include "util/common.h"
#include "arm.h"

View File

@ -1,7 +1,7 @@
#ifndef GBA_GPIO_H
#define GBA_GPIO_H
#include "common.h"
#include "util/common.h"
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)

View File

@ -1,7 +1,7 @@
#ifndef GBA_IO_H
#define GBA_IO_H
#include "common.h"
#include "util/common.h"
#include "gba.h"

View File

@ -1,5 +1,7 @@
#include "gba-memory.h"
#include "macros.h"
#include "gba-gpio.h"
#include "gba-io.h"
#include "gba-serialize.h"

View File

@ -1,9 +1,10 @@
#ifndef GBA_MEMORY_H
#define GBA_MEMORY_H
#include "common.h"
#include "util/common.h"
#include "arm.h"
#include "macros.h"
#include "gba-gpio.h"
#include "gba-savedata.h"

View File

@ -1,7 +1,7 @@
#ifndef GBA_RR_H
#define GBA_RR_H
#include "common.h"
#include "util/common.h"
struct GBA;
struct VDir;

View File

@ -343,7 +343,9 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
savedata->currentBank = &savedata->data[bank << 16];
if (bank > 0) {
savedata->type = SAVEDATA_FLASH1M;
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
if (savedata->vf) {
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
}
}
}

View File

@ -1,7 +1,7 @@
#ifndef GBA_SAVEDATA_H
#define GBA_SAVEDATA_H
#include "common.h"
#include "util/common.h"
struct VFile;

View File

@ -1,7 +1,7 @@
#ifndef GBA_SENSORS_H
#define GBA_SENSORS_H
#include "common.h"
#include "util/common.h"
struct GBARotationSource {
void (*sample)(struct GBARotationSource*);

View File

@ -103,17 +103,9 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
}
static struct VFile* _getStateVf(struct GBA* gba, struct VDir* dir, int slot, bool write) {
char path[PATH_MAX];
path[PATH_MAX - 1] = '\0';
struct VFile* vf;
if (!dir) {
snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot);
vf = VFileOpen(path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
} else {
snprintf(path, PATH_MAX - 1, "savestate.ss%d", slot);
vf = dir->openFile(dir, path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
}
return vf;
char suffix[5] = { '\0' };
snprintf(suffix, sizeof(suffix), ".ss%d", slot);
return VDirOptionalOpenFile(dir, gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
}
#ifdef USE_PNG

View File

@ -1,7 +1,7 @@
#ifndef GBA_SERIALIZE_H
#define GBA_SERIALIZE_H
#include "common.h"
#include "util/common.h"
#include "gba.h"

View File

@ -1,7 +1,7 @@
#ifndef GBA_SIO_H
#define GBA_SIO_H
#include "common.h"
#include "util/common.h"
enum GBASIOMode {
SIO_NORMAL_8 = 0,

View File

@ -276,35 +276,12 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
}
}
if (threadContext->stateDir) {
threadContext->save = threadContext->stateDir->openFile(threadContext->stateDir, "sram.sav", O_RDWR | O_CREAT);
}
if (!threadContext->rom) {
return false;
}
if (threadContext->fname && !threadContext->save) {
char* savedata = 0;
char* dotPoint = strrchr(threadContext->fname, '.');
if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
savedata = strdup(threadContext->fname);
dotPoint = strrchr(savedata, '.');
dotPoint[1] = 's';
dotPoint[2] = 'a';
dotPoint[3] = 'v';
dotPoint[4] = '\0';
} else if (dotPoint) {
savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
strcat(savedata, "sav");
} else {
savedata = malloc(strlen(threadContext->fname + 5) * sizeof(char));
sprintf(savedata, "%s.sav", threadContext->fname);
}
threadContext->save = VFileOpen(savedata, O_RDWR | O_CREAT);
free(savedata);
}
threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR);
MutexInit(&threadContext->stateMutex);
ConditionInit(&threadContext->stateCond);
@ -524,7 +501,7 @@ struct GBAThread* GBAThreadGetContext(void) {
void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
unsigned stride;
void* pixels = 0;
struct VFile* vf = threadContext->stateDir->openFile(threadContext->stateDir, "screenshot.png", O_CREAT | O_WRONLY);
struct VFile* vf = VDirOptionalOpenFile(threadContext->stateDir, threadContext->gba->activeFile, "screenshot", ".png", O_CREAT | O_TRUNC | O_WRONLY);
threadContext->gba->video.renderer->getPixels(threadContext->gba->video.renderer, &stride, &pixels);
png_structp png = PNGWriteOpen(vf);
png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);

View File

@ -1,7 +1,7 @@
#ifndef GBA_THREAD_H
#define GBA_THREAD_H
#include "common.h"
#include "util/common.h"
#include "gba.h"
#include "gba-input.h"

View File

@ -1,9 +1,10 @@
#ifndef GBA_VIDEO_H
#define GBA_VIDEO_H
#include "common.h"
#include "util/common.h"
#include "gba-memory.h"
#include "macros.h"
#ifdef COLOR_16_BIT
#define BYTES_PER_PIXEL 2

View File

@ -554,16 +554,17 @@ static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format
if (!gba) {
gba = threadContext->gba;
}
if (threadContext->logHandler) {
threadContext->logHandler(threadContext, level, format, args);
return;
}
}
if (gba && !(level & gba->logLevel) && level != GBA_LOG_FATAL) {
return;
}
if (threadContext && threadContext->logHandler) {
threadContext->logHandler(threadContext, level, format, args);
return;
}
vprintf(format, args);
printf("\n");

View File

@ -1,7 +1,7 @@
#ifndef GBA_H
#define GBA_H
#include "common.h"
#include "util/common.h"
#include "arm.h"
#include "debugger/debugger.h"

View File

@ -1,7 +1,7 @@
#ifndef HLE_BIOS_H
#define HLE_BIOS_H
#include "common.h"
#include "util/common.h"
extern const uint8_t hleBios[];

View File

@ -1,7 +1,7 @@
#ifndef VIDEO_GLSL_H
#define VIDEO_GLSL_H
#include "common.h"
#include "util/common.h"
#include "gba-video.h"

View File

@ -365,7 +365,7 @@ static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, stru
softwareRenderer->windows[activeWindow].endX = win->h.end;
if (win->h.end >= oldWindow.endX) {
// Trim off extra windows we've overwritten
for (++activeWindow; win->h.end >= softwareRenderer->windows[activeWindow].endX && softwareRenderer->nWindows > activeWindow; ++activeWindow) {
for (++activeWindow; softwareRenderer->nWindows > activeWindow + 1 && win->h.end >= softwareRenderer->windows[activeWindow].endX; ++activeWindow) {
softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
--softwareRenderer->nWindows;
}
@ -1193,6 +1193,9 @@ static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, stru
int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
flags |= FLAG_TARGET_2 * background->target2;
if (renderer->blda == 0x10 && renderer->bldb == 0) {
flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
}
uint32_t screenBase;
uint32_t charBase;
@ -1255,6 +1258,9 @@ static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, stru
int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
flags |= FLAG_TARGET_2 * background->target2; \
if (renderer->blda == 0x10 && renderer->bldb == 0) { \
flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
} \
int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
color_t* palette = renderer->normalPalette; \
if (variant) { \

View File

@ -1,7 +1,7 @@
#ifndef VIDEO_SOFTWARE_H
#define VIDEO_SOFTWARE_H
#include "common.h"
#include "util/common.h"
#include "gba-video.h"

View File

@ -1,7 +1,7 @@
#ifndef COMMAND_LINE_H
#define COMMAND_LINE_H
#include "common.h"
#include "util/common.h"
enum DebuggerType {
DEBUGGER_NONE = 0,

View File

@ -5,6 +5,7 @@
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <inttypes.h>
#include <sys/time.h>
#define PERF_OPTIONS "F:NPS:"
@ -96,9 +97,9 @@ int main(int argc, char** argv) {
} else {
rendererName = "software";
}
printf("%s,%i,%lli,%s\n", gameCode, frames, duration, rendererName);
printf("%s,%i,%" PRIu64 ",%s\n", gameCode, frames, duration, rendererName);
} else {
printf("%u frames in %lli microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
printf("%u frames in %" PRIu64 " microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
}
return 0;
@ -148,6 +149,7 @@ static void _GBAPerfShutdown(int signal) {
static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg) {
struct PerfOpts* opts = parser->opts;
errno = 0;
switch (option) {
case 'F':
opts->frames = strtoul(arg, 0, 10);

View File

@ -14,10 +14,11 @@ endif()
if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND)
find_package(SDL 1.2)
set(SDL_VERSION "1.2" PARENT_SCOPE)
endif()
if (NOT SDL2_FOUND AND NOT SDL_FOUND)
set(BUILD_SDL OFF)
set(BUILD_SDL OFF PARENT_SCOPE)
return()
endif()

View File

@ -1,7 +1,7 @@
#ifndef SDL_AUDIO_H
#define SDL_AUDIO_H
#include "common.h"
#include "util/common.h"
#include <SDL.h>

View File

@ -1,7 +1,7 @@
#ifndef SDL_EVENTS_H
#define SDL_EVENTS_H
#include "common.h"
#include "util/common.h"
#include "gba-thread.h"

View File

@ -1,7 +1,7 @@
#ifndef CIRCLE_BUFFER_H
#define CIRCLE_BUFFER_H
#include "common.h"
#include "util/common.h"
struct CircleBuffer {
void* data;

19
src/util/common.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef COMMON_H
#define COMMON_H
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define UNUSED(V) (void)(V)
#endif

View File

@ -1,8 +1,7 @@
#ifndef CRC32_H
#define CRC32_H
#include <stdint.h>
#include <string.h>
#include "util/common.h"
struct VFile;

View File

@ -1,7 +1,7 @@
#ifndef MEMORY_H
#define MEMORY_H
#include "common.h"
#include "util/common.h"
void* anonymousMemoryMap(size_t size);
void mappedMemoryFree(void* memory, size_t size);

View File

@ -1,7 +1,7 @@
#ifndef PATCH_IPS_H
#define PATCH_IPS_H
#include "common.h"
#include "util/common.h"
struct Patch;

View File

@ -1,7 +1,7 @@
#ifndef PATCH_UPS_H
#define PATCH_UPS_H
#include "common.h"
#include "util/common.h"
struct Patch;

View File

@ -1,7 +1,7 @@
#ifndef PATCH_H
#define PATCH_H
#include "common.h"
#include "util/common.h"
struct VFile;

View File

@ -1,7 +1,7 @@
#ifndef PNG_IO_H
#define PNG_IO_H
#include "common.h"
#include "util/common.h"
#ifdef USE_PNG

View File

@ -1,7 +1,7 @@
#ifndef SOCKET_H
#define SOCKET_H
#include "common.h"
#include "util/common.h"
#ifdef __cplusplus
#define restrict __restrict__

View File

@ -1,7 +1,7 @@
#ifndef THREADING_H
#define THREADING_H
#include "common.h"
#include "util/common.h"
#ifdef USE_PTHREADS
#include <pthread.h>

View File

@ -183,6 +183,34 @@ struct VDir* VDirOpen(const char* path) {
return &vd->d;
}
struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode) {
char path[PATH_MAX];
path[PATH_MAX - 1] = '\0';
struct VFile* vf;
if (!dir) {
if (!realPath) {
return 0;
}
char* dotPoint = strrchr(realPath, '.');
if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
return 0;
}
if (dotPoint > strrchr(realPath, '/')) {
int len = dotPoint - realPath;
strncpy(path, realPath, len);
path[len] = 0;
strncat(path + len, suffix, PATH_MAX - len - 1);
} else {
snprintf(path, PATH_MAX - 1, "%s%s", realPath, suffix);
}
vf = VFileOpen(path, mode);
} else {
snprintf(path, PATH_MAX - 1, "%s%s", prefix, suffix);
vf = dir->openFile(dir, path, mode);
}
return vf;
}
bool _vdClose(struct VDir* vd) {
struct VDirDE* vdde = (struct VDirDE*) vd;
if (closedir(vdde->de) < 0) {

View File

@ -1,7 +1,7 @@
#ifndef VFS_H
#define VFS_H
#include "common.h"
#include "util/common.h"
enum {
MAP_READ = 1,
@ -39,4 +39,6 @@ struct VDir* VDirOpen(const char* path);
struct VDir* VDirOpenZip(const char* path, int flags);
#endif
struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode);
#endif

123
tools/perf.py Executable file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env python
from __future__ import print_function
import argparse
import csv
import os
import signal
import subprocess
import sys
import time
class PerfTest(object):
EXECUTABLE = 'mgba-perf'
def __init__(self, rom, renderer='software'):
self.rom = rom
self.renderer = renderer
self.results = None
self.name = 'Perf Test: {}'.format(rom)
def get_args(self):
return []
def wait(self, proc):
pass
def run(self, cwd):
args = [os.path.join(os.getcwd(), self.EXECUTABLE), '-P']
args.extend(self.get_args())
if self.renderer != 'software':
args.append('-N')
args.append(self.rom)
env = {}
if 'LD_LIBRARY_PATH' in os.environ:
env['LD_LIBRARY_PATH'] = os.path.abspath(os.environ['LD_LIBRARY_PATH'])
env['DYLD_LIBRARY_PATH'] = env['LD_LIBRARY_PATH'] # Fake it on OS X
proc = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd, universal_newlines=True, env=env)
try:
self.wait(proc)
proc.wait()
except:
proc.kill()
raise
if proc.returncode < 0:
print('Game crashed!', file=sys.stderr)
return
reader = csv.DictReader(proc.stdout)
self.results = next(reader)
class WallClockTest(PerfTest):
def __init__(self, rom, duration, renderer='software'):
super(WallClockTest, self).__init__(rom, renderer)
self.duration = duration
self.name = 'Wall-Clock Test ({} seconds, {} renderer): {}'.format(duration, renderer, rom)
def wait(self, proc):
time.sleep(self.duration)
proc.send_signal(signal.SIGINT)
class GameClockTest(PerfTest):
def __init__(self, rom, frames, renderer='software'):
super(GameClockTest, self).__init__(rom, renderer)
self.frames = frames
self.name = 'Game-Clock Test ({} frames, {} renderer): {}'.format(frames, renderer, rom)
def get_args(self):
return ['-F', str(self.frames)]
class Suite(object):
def __init__(self, cwd, wall=None, game=None, renderer='software'):
self.cwd = cwd
self.tests = []
self.wall = wall
self.game = game
self.renderer = renderer
def collect_tests(self):
roms = []
for f in os.listdir(self.cwd):
if f.endswith('.gba'):
roms.append(f)
roms.sort()
for rom in roms:
self.add_tests(rom)
def add_tests(self, rom):
if self.wall:
self.tests.append(WallClockTest(rom, self.wall, renderer=self.renderer))
if self.game:
self.tests.append(GameClockTest(rom, self.game, renderer=self.renderer))
def run(self):
results = []
for test in self.tests:
print('Running test {}'.format(test.name), file=sys.stderr)
try:
test.run(self.cwd)
except KeyboardInterrupt:
print('Interrupted, returning early...', file=sys.stderr)
return results
if test.results:
results.append(test.results)
return results
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-w', '--wall-time', type=float, default=0, metavar='TIME', help='wall-clock time')
parser.add_argument('-g', '--game-frames', type=int, default=0, metavar='FRAMES', help='game-clock frames')
parser.add_argument('-N', '--disable-renderer', action='store_const', const=True, help='disable video rendering')
parser.add_argument('-o', '--out', metavar='FILE', help='output file path')
parser.add_argument('directory', help='directory containing ROM files')
args = parser.parse_args()
s = Suite(args.directory, wall=args.wall_time, game=args.game_frames, renderer=None if args.disable_renderer else 'software')
s.collect_tests()
results = s.run()
fout = sys.stdout
if args.out:
fout = open(args.out, 'w')
writer = csv.DictWriter(fout, results[0].keys())
writer.writeheader()
writer.writerows(results)
if fout is not sys.stdout:
fout.close()