Merge branch 'master' into optimization/idle-loop-detection

Conflicts:
	CHANGES
	src/gba/gba-overrides.c
	src/gba/gba-thread.h
	src/gba/gba.c
This commit is contained in:
Jeffrey Pfau 2015-01-16 02:55:21 -08:00
commit d49a9a84f7
18 changed files with 292 additions and 156 deletions

16
CHANGES
View File

@ -19,6 +19,7 @@ Features:
- Save directory of last loaded file
- Support BPS patches
- Automatically detect and optimize out idle loops
- Configurable game overrides
Bugfixes:
- Qt: Fix issue with set frame sizes being the wrong height
- Qt: Fix emulator crashing when full screen if a game is not running
@ -51,6 +52,17 @@ Bugfixes:
- GBA Memory: Filter out top nybble of DMA addresses
- Debugger: Fix binary print putting spaces between digits
- GBA BIOS: Fix LZ77UnCompVram to use 16-bit loads from decompressed memory
- GBA BIOS: Fix HuffUnComp to work when games pass an invalid bit length
- GBA BIOS: Fix GetBiosChecksum to return the value of a real GBA, regardless of used BIOS
- GBA Memory: Properly bounds-check VRAM accesses
- GBA Memory: Fix initial DMA state
- GBA BIOS: Fix BIOS prefetch after returning from an IRQ
- GBA BIOS: Fix BIOS prefetch after reset
- GBA Memory: Fix alignment of open bus 8- and 16-bit loads
- GBA BIOS: Fix HuffUnComp boundary conditions
- GBA Video: Fix mode 0 being able to read tiles above appropriate tile range
- GBA Audio: Properly initialize audio FIFO channels
- Util: Fix SOCKET_FAILED macro
Misc:
- Qt: Disable sync to video by default
- GBA: Exit cleanly on FATAL if the port supports it
@ -61,6 +73,10 @@ Misc:
- GBA Memory: Implement 16- and 32-bit loads from SRAM
- Qt: Clear active buttons when focus is lost
- GBA Memory: Simplify memory API and use fixed bus width
- GBA Video: Start video at the last scanline instead of the first
- Debugger: Watchpoints now work on STM/LDM instructions
- GBA: Improve accuracy of event timing
- Debugger: Clean up GDB stub network interfacing
0.1.0: (2014-12-13)
- Initial release

View File

@ -53,15 +53,39 @@ static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReas
static void _gdbStubPoll(struct ARMDebugger* debugger) {
struct GDBStub* stub = (struct GDBStub*) debugger;
while (stub->d.state == DEBUGGER_PAUSED) {
if (!SOCKET_FAILED(stub->connection)) {
if (!SocketSetBlocking(stub->connection, 1)) {
GDBStubHangup(stub);
return;
}
}
GDBStubUpdate(stub);
--stub->untilPoll;
if (stub->untilPoll > 0) {
return;
}
stub->untilPoll = GDB_STUB_INTERVAL;
if (stub->shouldBlock) {
stub->shouldBlock = false;
if (!SocketSetBlocking(stub->socket, false)) {
GDBStubHangup(stub);
return;
}
if (!SOCKET_FAILED(stub->connection) && !SocketSetBlocking(stub->connection, false)) {
GDBStubHangup(stub);
return;
}
}
GDBStubUpdate(stub);
}
static void _gdbStubWait(struct ARMDebugger* debugger) {
struct GDBStub* stub = (struct GDBStub*) debugger;
if (!stub->shouldBlock) {
stub->shouldBlock = true;
if (!SocketSetBlocking(stub->socket, true)) {
GDBStubHangup(stub);
return;
}
if (!SOCKET_FAILED(stub->connection) && !SocketSetBlocking(stub->connection, true)) {
GDBStubHangup(stub);
return;
}
}
GDBStubUpdate(stub);
}
static void _ack(struct GDBStub* stub) {
@ -169,7 +193,8 @@ static void _writeHostInfo(struct GDBStub* stub) {
}
static void _continue(struct GDBStub* stub, const char* message) {
stub->d.state = DEBUGGER_RUNNING;
stub->d.state = DEBUGGER_CUSTOM;
stub->untilPoll = GDB_STUB_INTERVAL;
if (!SOCKET_FAILED(stub->connection)) {
if (!SocketSetBlocking(stub->connection, 0)) {
GDBStubHangup(stub);
@ -441,9 +466,11 @@ void GDBStubCreate(struct GDBStub* stub) {
stub->connection = INVALID_SOCKET;
stub->d.init = 0;
stub->d.deinit = _gdbStubDeinit;
stub->d.paused = _gdbStubPoll;
stub->d.paused = _gdbStubWait;
stub->d.entered = _gdbStubEntered;
stub->d.custom = _gdbStubPoll;
stub->d.log = 0;
stub->untilPoll = GDB_STUB_INTERVAL;
}
int GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
@ -461,9 +488,6 @@ int GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddr
if (err) {
goto cleanup;
}
if (!SocketSetBlocking(stub->socket, 0)) {
goto cleanup;
}
return 1;
@ -501,9 +525,6 @@ void GDBStubUpdate(struct GDBStub* stub) {
if (stub->connection == INVALID_SOCKET) {
stub->connection = SocketAccept(stub->socket, 0);
if (!SOCKET_FAILED(stub->connection)) {
if (!SocketSetBlocking(stub->connection, 0)) {
goto connectionLost;
}
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
return;

View File

@ -13,6 +13,7 @@
#include "util/socket.h"
#define GDB_STUB_MAX_LINE 1200
#define GDB_STUB_INTERVAL 32
enum GDBStubAckState {
GDB_ACK_PENDING = 0,
@ -30,6 +31,9 @@ struct GDBStub {
Socket socket;
Socket connection;
bool shouldBlock;
int untilPoll;
};
void GDBStubCreate(struct GDBStub*);

View File

@ -10,6 +10,11 @@
#include <string.h>
static bool _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t address, int width);
static uint32_t _popcount32(unsigned bits) {
bits = bits - ((bits >> 1) & 0x55555555);
bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
}
#define FIND_DEBUGGER(DEBUGGER, CPU) \
{ \
@ -40,12 +45,37 @@ static bool _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t addr
return debugger->originalMemory.NAME(cpu, ARGS); \
}
#define CREATE_MULTIPLE_WATCHPOINT_SHIM(NAME) \
static uint32_t ARMDebuggerShim_ ## NAME (struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { \
struct ARMDebugger* debugger; \
FIND_DEBUGGER(debugger, cpu); \
uint32_t popcount = _popcount32(mask); \
int offset = 4; \
int base = address; \
if (direction & LSM_D) { \
offset = -4; \
base -= (popcount << 2) - 4; \
} \
if (direction & LSM_B) { \
base += offset; \
} \
unsigned i; \
for (i = 0; i < popcount; ++i) { \
if (_checkWatchpoints(debugger->watchpoints, base + 4 * i, 4)) { \
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT); \
} \
} \
return debugger->originalMemory.NAME(cpu, address, mask, direction, cycleCounter); \
}
CREATE_WATCHPOINT_SHIM(load32, 4, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
CREATE_WATCHPOINT_SHIM(load16, 2, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
CREATE_WATCHPOINT_SHIM(load8, 1, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
CREATE_WATCHPOINT_SHIM(store32, 4, void, (struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter), address, value, cycleCounter)
CREATE_WATCHPOINT_SHIM(store16, 2, void, (struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter), address, value, cycleCounter)
CREATE_WATCHPOINT_SHIM(store8, 1, void, (struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter), address, value, cycleCounter)
CREATE_MULTIPLE_WATCHPOINT_SHIM(loadMultiple)
CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple)
CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address)
static bool _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t address, int width) {
@ -66,6 +96,8 @@ void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger) {
debugger->cpu->memory.load32 = ARMDebuggerShim_load32;
debugger->cpu->memory.load16 = ARMDebuggerShim_load16;
debugger->cpu->memory.load8 = ARMDebuggerShim_load8;
debugger->cpu->memory.storeMultiple = ARMDebuggerShim_storeMultiple;
debugger->cpu->memory.loadMultiple = ARMDebuggerShim_loadMultiple;
debugger->cpu->memory.setActiveRegion = ARMDebuggerShim_setActiveRegion;
}
@ -76,5 +108,7 @@ void ARMDebuggerRemoveMemoryShim(struct ARMDebugger* debugger) {
debugger->cpu->memory.load32 = debugger->originalMemory.load32;
debugger->cpu->memory.load16 = debugger->originalMemory.load16;
debugger->cpu->memory.load8 = debugger->originalMemory.load8;
debugger->cpu->memory.storeMultiple = debugger->originalMemory.storeMultiple;
debugger->cpu->memory.loadMultiple = debugger->originalMemory.loadMultiple;
debugger->cpu->memory.setActiveRegion = debugger->originalMemory.setActiveRegion;
}

View File

@ -53,8 +53,8 @@ void GBAAudioReset(struct GBAAudio* audio) {
audio->ch2 = (struct GBAAudioChannel2) { .envelope = { .nextStep = INT_MAX } };
audio->ch3 = (struct GBAAudioChannel3) { .bank = { .bank = 0 } };
audio->ch4 = (struct GBAAudioChannel4) { .envelope = { .nextStep = INT_MAX } };
audio->chA.dmaSource = 0;
audio->chB.dmaSource = 0;
audio->chA.dmaSource = 1;
audio->chB.dmaSource = 2;
audio->chA.sample = 0;
audio->chB.sample = 0;
audio->eventDiff = 0;

View File

@ -215,7 +215,10 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
ARMRaiseSWI(cpu);
break;
case 0xD:
cpu->gprs[0] = GBAChecksum(gba->memory.bios, SIZE_BIOS);
cpu->gprs[0] = GBA_BIOS_CHECKSUM;
cpu->gprs[1] = 1;
cpu->gprs[3] = SIZE_BIOS;
break;
case 0xE:
_BgAffineSet(gba);
break;
@ -288,6 +291,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
default:
GBALog(gba, GBA_LOG_STUB, "Stub software interrupt: %02X", immediate);
}
gba->memory.biosPrefetch = 0xE3A02004;
}
void GBASwi32(struct ARMCore* cpu, int immediate) {
@ -385,12 +389,14 @@ static void _unHuffman(struct GBA* gba) {
uint32_t header = cpu->memory.load32(cpu, source, 0);
int remaining = header >> 8;
int bits = header & 0xF;
if (32 % bits) {
if (bits == 0) {
GBALog(gba, GBA_LOG_GAME_ERROR, "Invalid Huffman bits");
bits = 8;
}
if (32 % bits || bits == 1) {
GBALog(gba, GBA_LOG_STUB, "Unimplemented unaligned Huffman");
return;
}
int padding = (4 - remaining) & 0x3;
remaining &= 0xFFFFFFFC;
// We assume the signature byte (0x20) is correct
int treesize = (cpu->memory.load8(cpu, source + 4, 0) << 1) + 1;
int block = 0;
@ -441,9 +447,6 @@ static void _unHuffman(struct GBA* gba) {
}
}
if (padding) {
cpu->memory.store32(cpu, dest, block, 0);
}
cpu->gprs[0] = source;
cpu->gprs[1] = dest;
}

View File

@ -101,9 +101,10 @@ void GBAMemoryReset(struct GBA* gba) {
memset(gba->memory.dma, 0, sizeof(gba->memory.dma));
int i;
for (i = 0; i < 4; ++i) {
gba->memory.dma[i].count = 0x10000;
gba->memory.dma[i].count = 0x4000;
gba->memory.dma[i].nextEvent = INT_MAX;
}
gba->memory.dma[3].count = 0x10000;
gba->memory.activeDMA = -1;
gba->memory.nextDMA = INT_MAX;
gba->memory.eventDiff = 0;
@ -300,7 +301,11 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
++wait;
#define LOAD_VRAM \
LOAD_32(value, address & 0x0001FFFF, gba->video.renderer->vram); \
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
LOAD_32(value, address & 0x0001FFFF, gba->video.renderer->vram); \
} else { \
LOAD_32(value, address & 0x00017FFF, gba->video.renderer->vram); \
} \
++wait;
#define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 1), gba->video.oam.raw);
@ -388,14 +393,14 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
LOAD_16(value, address, memory->bios);
} else {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad BIOS Load16: 0x%08X", address);
value = memory->biosPrefetch & 0xFFFF;
LOAD_16(value, address & 2, &memory->biosPrefetch);
}
} else {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address);
if (cpu->cycles >= cpu->nextEvent) {
value = gba->bus & 0xFFFF;
LOAD_16(value, address & 2, &gba->bus);
} else {
value = cpu->prefetch[1] & 0xFFFF;
LOAD_16(value, address & 2, &cpu->prefetch[1]);
}
}
break;
@ -413,7 +418,11 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
LOAD_16(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette);
break;
case REGION_VRAM:
LOAD_16(value, address & 0x0001FFFF, gba->video.renderer->vram);
if ((address & 0x0001FFFF) < SIZE_VRAM) {
LOAD_16(value, address & 0x0001FFFF, gba->video.renderer->vram);
} else {
LOAD_16(value, address & 0x00017FFF, gba->video.renderer->vram);
}
break;
case REGION_OAM:
LOAD_16(value, address & (SIZE_OAM - 1), gba->video.oam.raw);
@ -451,9 +460,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
default:
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address);
if (cpu->cycles >= cpu->nextEvent) {
value = gba->bus;
LOAD_16(value, address & 2, &gba->bus);
} else {
value = cpu->prefetch[1];
LOAD_16(value, address & 2, &cpu->prefetch[1]);
}
break;
}
@ -479,14 +488,14 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
value = ((int8_t*) memory->bios)[address];
} else {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad BIOS Load8: 0x%08X", address);
value = memory->biosPrefetch;
value = ((uint8_t*) &memory->biosPrefetch)[address & 3];
}
} else {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load8: 0x%08x", address);
if (cpu->cycles >= cpu->nextEvent) {
value = gba->bus;
value = ((uint8_t*) &gba->bus)[address & 3];
} else {
value = cpu->prefetch[1];
value = ((uint8_t*) &cpu->prefetch[1])[address & 3];
}
}
break;
@ -504,7 +513,11 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
value = ((int8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)];
break;
case REGION_VRAM:
value = ((int8_t*) gba->video.renderer->vram)[address & 0x0001FFFF];
if ((address & 0x0001FFFF) < SIZE_VRAM) {
value = ((int8_t*) gba->video.renderer->vram)[address & 0x0001FFFF];
} else {
value = ((int8_t*) gba->video.renderer->vram)[address & 0x00017FFF];
}
break;
case REGION_OAM:
GBALog(gba, GBA_LOG_STUB, "Unimplemented memory Load8: 0x%08X", address);
@ -544,9 +557,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
default:
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load8: 0x%08x", address);
if (cpu->cycles >= cpu->nextEvent) {
value = gba->bus;
value = ((uint8_t*) &gba->bus)[address & 3];
} else {
value = cpu->prefetch[1];
value = ((uint8_t*) &cpu->prefetch[1])[address & 3];
}
break;
}
@ -574,9 +587,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 1), value);
#define STORE_VRAM \
if ((address & OFFSET_MASK) < SIZE_VRAM) { \
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
STORE_32(value, address & 0x0001FFFF, gba->video.renderer->vram); \
} else if ((address & OFFSET_MASK) < 0x00020000) { \
} else { \
STORE_32(value, address & 0x00017FFF, gba->video.renderer->vram); \
} \
++wait;
@ -663,9 +676,9 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 1), value);
break;
case REGION_VRAM:
if ((address & OFFSET_MASK) < SIZE_VRAM) {
if ((address & 0x0001FFFF) < SIZE_VRAM) {
STORE_16(value, address & 0x0001FFFF, gba->video.renderer->vram);
} else if ((address & OFFSET_MASK) < 0x00020000) {
} else {
STORE_16(value, address & 0x00017FFF, gba->video.renderer->vram);
}
break;

View File

@ -40,6 +40,10 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
memcpy(state->cpu.bankedRegisters, gba->cpu->bankedRegisters, 6 * 7 * sizeof(int32_t));
memcpy(state->cpu.bankedSPSRs, gba->cpu->bankedSPSRs, 6 * sizeof(int32_t));
state->biosPrefetch = gba->memory.biosPrefetch;
state->cpuPrefetch[0] = gba->cpu->prefetch[0];
state->cpuPrefetch[1] = gba->cpu->prefetch[1];
GBAMemorySerialize(&gba->memory, state);
GBAIOSerialize(gba, state);
GBAVideoSerialize(&gba->video, state);
@ -80,14 +84,29 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
memcpy(gba->cpu->bankedSPSRs, state->cpu.bankedSPSRs, 6 * sizeof(int32_t));
gba->cpu->privilegeMode = gba->cpu->cpsr.priv;
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
if (state->biosPrefetch) {
gba->memory.biosPrefetch = state->biosPrefetch;
}
if (gba->cpu->cpsr.t) {
gba->cpu->executionMode = MODE_THUMB;
LOAD_16(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
LOAD_16(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) {
gba->cpu->prefetch[0] = state->cpuPrefetch[0] & 0xFFFF;
gba->cpu->prefetch[1] = state->cpuPrefetch[1] & 0xFFFF;
} else {
// Maintain backwards compat
LOAD_16(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_THUMB) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
LOAD_16(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
}
} else {
gba->cpu->executionMode = MODE_ARM;
LOAD_32(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
LOAD_32(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) {
gba->cpu->prefetch[0] = state->cpuPrefetch[0];
gba->cpu->prefetch[1] = state->cpuPrefetch[1];
} else {
// Maintain backwards compat
LOAD_32(gba->cpu->prefetch[0], (gba->cpu->gprs[ARM_PC] - WORD_SIZE_ARM) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
LOAD_32(gba->cpu->prefetch[1], (gba->cpu->gprs[ARM_PC]) & gba->cpu->memory.activeMask, gba->cpu->memory.activeRegion);
}
}
GBAMemoryDeserialize(&gba->memory, state);

View File

@ -150,7 +150,11 @@ extern const uint32_t GBA_SAVESTATE_MAGIC;
* | 0x002C1 - 0x002C3: Flags
* | bits 0 - 1: Tilt state machine
* | bits 2 - 31: Reserved
* 0x002C4 - 0x002FF: Reserved (leave zero)
* 0x002C4 - 0x002F3: Reserved (leave zero)
* 0x002F4 - 0x002FF: Prefetch
* | 0x002F4 - 0x002F7: GBA BIOS bus prefetch
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
* | 0x002FC - 0x002FF: CPU prefetch (fetch slot)
* 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream)
* 0x00304 - 0x003FF: Reserved (leave zero)
* 0x00400 - 0x007FF: I/O memory
@ -267,7 +271,10 @@ struct GBASerializedState {
unsigned : 22;
} gpio;
uint32_t reservedGpio[15];
uint32_t reservedGpio[12];
uint32_t biosPrefetch;
uint32_t cpuPrefetch[2];
uint32_t associatedStreamId;

View File

@ -42,7 +42,7 @@ void GBAVideoInit(struct GBAVideo* video) {
void GBAVideoReset(struct GBAVideo* video) {
video->dispstat = 0;
video->vcount = 0;
video->vcount = VIDEO_VERTICAL_TOTAL_PIXELS - 1;
video->lastHblank = 0;
video->nextHblank = VIDEO_HDRAW_LENGTH;

View File

@ -21,8 +21,8 @@
const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000;
const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
static const size_t GBA_ROM_MAGIC_OFFSET = 2;
static const uint8_t GBA_ROM_MAGIC[] = { 0x00, 0xEA };
static const size_t GBA_ROM_MAGIC_OFFSET = 3;
static const uint8_t GBA_ROM_MAGIC[] = { 0xEA };
static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component);
static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh);
@ -145,7 +145,7 @@ void GBASkipBIOS(struct ARMCore* cpu) {
static void GBAProcessEvents(struct ARMCore* cpu) {
do {
struct GBA* gba = (struct GBA*) cpu->master;
int32_t cycles = cpu->cycles;
int32_t cycles = cpu->nextEvent;
int32_t nextEvent = INT_MAX;
int32_t testEvent;

View File

@ -1,55 +1,51 @@
/* Copyright (c) 2013-2014 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "hle-bios.h"
#include "gba-memory.h"
const uint8_t hleBios[SIZE_BIOS] = {
0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x05, 0x00, 0x00, 0xea,
0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x07, 0x00, 0x00, 0xea,
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
0x26, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3,
0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02,
0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x84, 0xc0, 0xa0, 0xe3,
0x0b, 0xb1, 0x9c, 0xe7, 0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1,
0x00, 0x10, 0x2d, 0xe9, 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3,
0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1,
0x1b, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3,
0x00, 0x10, 0xbd, 0xe8, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8,
0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3,
0x28, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0x00, 0x00, 0x5d, 0xe3,
0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9,
0x02, 0xb0, 0x5e, 0xe5, 0x8c, 0xc0, 0xa0, 0xe3, 0x0b, 0xb1, 0x9c, 0xe7,
0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0x10, 0x2d, 0xe9,
0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, 0x0c, 0xf0, 0x29, 0xe1,
0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1, 0x1b, 0xff, 0x2f, 0x11,
0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x10, 0xbd, 0xe8,
0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1,
0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00,
0xe8, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3,
0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
0x04, 0xf0, 0x5e, 0xe2, 0x01, 0x00, 0xa0, 0xe3, 0x01, 0x10, 0xa0, 0xe3,
0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3,
0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3, 0x03, 0x00, 0x00, 0x0a,
0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0xc3, 0xe1, 0xb8, 0x30, 0x4c, 0xe1,
0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5, 0xb8, 0x30, 0x5c, 0xe1,
0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, 0xb8, 0x30, 0x4c, 0x11,
0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a, 0x0c, 0x80, 0xbd, 0xe8,
0x00, 0x40, 0x2d, 0xe9, 0x02, 0x36, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3,
0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a,
0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8, 0x03, 0x00, 0x51, 0xe1,
0x04, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x16, 0x00, 0x00, 0xea,
0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, 0xa3, 0x35, 0x81, 0xe0,
0xb0, 0x20, 0xd0, 0xe1, 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xc1, 0xb0,
0xfc, 0xff, 0xff, 0xba, 0x0e, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3,
0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x03, 0x00, 0x51, 0xe1,
0x04, 0x00, 0xb0, 0xb8, 0x04, 0x00, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
0x06, 0x00, 0x00, 0xea, 0xa3, 0x35, 0x81, 0xe0, 0x01, 0x00, 0xc0, 0xe3,
0x01, 0x10, 0xc1, 0xe3, 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xd0, 0xb0,
0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba, 0x00, 0x80, 0xbd, 0xe8,
0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1,
0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5,
0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1,
0x03, 0x70, 0xa0, 0xe1, 0x03, 0x80, 0xa0, 0xe1, 0x03, 0x90, 0xa0, 0xe1,
0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8,
0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1,
0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
0xf0, 0x87, 0xbd, 0xe8
0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00,
0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2,
0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2,
0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5, 0x01, 0x00, 0xa0, 0xe3,
0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3,
0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3,
0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0xc3, 0xe1,
0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5,
0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10,
0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a,
0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x02, 0x36, 0xa0, 0xe1,
0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3,
0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8,
0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba,
0x16, 0x00, 0x00, 0xea, 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3,
0xa3, 0x35, 0x81, 0xe0, 0xb0, 0x20, 0xd0, 0xe1, 0x03, 0x00, 0x51, 0xe1,
0xb2, 0x20, 0xc1, 0xb0, 0xfc, 0xff, 0xff, 0xba, 0x0e, 0x00, 0x00, 0xea,
0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0,
0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xb0, 0xb8, 0x04, 0x00, 0xa1, 0xb8,
0xfb, 0xff, 0xff, 0xba, 0x06, 0x00, 0x00, 0xea, 0xa3, 0x35, 0x81, 0xe0,
0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, 0x03, 0x00, 0x51, 0xe1,
0xb2, 0x20, 0xd0, 0xb0, 0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba,
0x00, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3,
0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a,
0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1,
0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1, 0x03, 0x80, 0xa0, 0xe1,
0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1,
0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea,
0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8,
0xfb, 0xff, 0xff, 0xba, 0xf0, 0x87, 0xbd, 0xe8
};

View File

@ -18,6 +18,8 @@ b fiqBase
resetBase:
mov pc, #0x8000000
.word 0
.word 0xE129F000
swiBase:
cmp sp, #0
@ -68,6 +70,8 @@ add lr, pc, #0
ldr pc, [r0, #-4]
ldmfd sp!, {r0-r3, r12, lr}
subs pc, lr, #4
.word 0
.word 0xE55EC002
VBlankIntrWait:
mov r0, #1

View File

@ -1010,6 +1010,10 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
palette = &mainPalette[paletteData]; \
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
if (UNLIKELY(charBase >= 0x10000)) { \
pixel += 8; \
continue; \
} \
LOAD_32(tileData, charBase, vram); \
if (tileData) { \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
@ -1058,35 +1062,43 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
int end2 = end - 4; \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
int shift = inX & 0x3; \
if (end2 > outX) { \
LOAD_32(tileData, charBase, vram); \
tileData >>= 8 * shift; \
shift = 0; \
for (; outX < end2; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
if (LIKELY(charBase < 0x10000)) { \
if (end2 > outX) { \
LOAD_32(tileData, charBase, vram); \
tileData >>= 8 * shift; \
shift = 0; \
for (; outX < end2; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
} \
} \
} \
\
LOAD_32(tileData, charBase + 4, vram); \
tileData >>= 8 * shift; \
for (; outX < end; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
if (LIKELY(charBase < 0x10000)) { \
LOAD_32(tileData, charBase + 4, vram); \
tileData >>= 8 * shift; \
for (; outX < end; ++outX, ++pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
} \
} \
} else { \
int start = outX; \
outX = end - 1; \
pixel = &renderer->row[outX]; \
if (end2 > start) { \
LOAD_32(tileData, charBase, vram); \
for (; outX >= end2; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
if (LIKELY(charBase < 0x10000)) { \
if (end2 > start) { \
LOAD_32(tileData, charBase, vram); \
for (; outX >= end2; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
} \
charBase += 4; \
} \
charBase += 4; \
} \
\
LOAD_32(tileData, charBase, vram); \
for (; outX >= renderer->start; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
if (LIKELY(charBase < 0x10000)) { \
LOAD_32(tileData, charBase, vram); \
for (; outX >= renderer->start; --outX, --pixel) { \
BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
} \
} \
outX = end; \
pixel = &renderer->row[outX]; \
@ -1094,6 +1106,9 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
if (UNLIKELY(charBase >= 0x10000)) { \
return; \
} \
int end = mod8 - 4; \
pixel = &renderer->row[outX]; \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
@ -1139,6 +1154,10 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
for (; tileX < tileEnd; ++tileX) { \
BACKGROUND_TEXT_SELECT_CHARACTER; \
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
if (UNLIKELY(charBase >= 0x10000)) { \
pixel += 8; \
continue; \
} \
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
LOAD_32(tileData, charBase, vram); \
if (tileData) { \

View File

@ -112,46 +112,43 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
}
return;
}
switch (event->keysym.sym) {
case SDLK_F11:
if (event->type == SDL_KEYDOWN && context->debugger) {
ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL);
}
if (event->keysym.sym == SDLK_TAB) {
context->sync.audioWait = event->type != SDL_KEYDOWN;
return;
}
if (event->type == SDL_KEYDOWN) {
switch (event->keysym.sym) {
case SDLK_F11:
if (context->debugger) {
ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL);
}
return;
#ifdef USE_PNG
case SDLK_F12:
if (event->type == SDL_KEYDOWN) {
case SDLK_F12:
GBAThreadInterrupt(context);
GBAThreadTakeScreenshot(context);
GBAThreadContinue(context);
}
return;
return;
#endif
case SDLK_TAB:
context->sync.audioWait = event->type != SDL_KEYDOWN;
return;
case SDLK_BACKSLASH:
if (event->type == SDL_KEYDOWN) {
case SDLK_BACKSLASH:
GBAThreadPause(context);
context->frameCallback = _pauseAfterFrame;
GBAThreadUnpause(context);
}
return;
case SDLK_LEFTBRACKET:
GBAThreadInterrupt(context);
GBARewind(context, 10);
GBAThreadContinue(context);
return;
case SDLK_ESCAPE:
GBAThreadInterrupt(context);
if (context->gba->rr) {
GBARRStopPlaying(context->gba->rr);
GBARRStopRecording(context->gba->rr);
}
GBAThreadContinue(context);
return;
default:
if (event->type == SDL_KEYDOWN) {
return;
case SDLK_BACKQUOTE:
GBAThreadInterrupt(context);
GBARewind(context, 10);
GBAThreadContinue(context);
return;
case SDLK_ESCAPE:
GBAThreadInterrupt(context);
if (context->gba->rr) {
GBARRStopPlaying(context->gba->rr);
GBARRStopRecording(context->gba->rr);
}
GBAThreadContinue(context);
return;
default:
if ((event->keysym.mod & GUI_MOD) && (event->keysym.mod & GUI_MOD) == event->keysym.mod) {
switch (event->keysym.sym) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
@ -238,8 +235,8 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
break;
}
}
return;
}
return;
}
}

View File

@ -129,6 +129,9 @@ bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, siz
if (_decodeLength(patch->vf) != outSize) {
return false;
}
if (inSize > SSIZE_MAX || outSize > SSIZE_MAX) {
return false;
}
size_t metadataLength = _decodeLength(patch->vf);
patch->vf->seek(patch->vf, metadataLength, SEEK_CUR); // Skip metadata
size_t writeLocation = 0;
@ -153,7 +156,7 @@ bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, siz
break;
case 0x1:
// TargetRead
if (patch->vf->read(patch->vf, &writeBuffer[writeLocation], length) != length) {
if (patch->vf->read(patch->vf, &writeBuffer[writeLocation], length) != (ssize_t) length) {
return false;
}
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation], length);
@ -167,7 +170,7 @@ bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, siz
} else {
readSourceLocation += readOffset >> 1;
}
if (readSourceLocation < 0 || readSourceLocation > inSize) {
if (readSourceLocation < 0 || readSourceLocation > (ssize_t) inSize) {
return false;
}
memmove(&writeBuffer[writeLocation], &readBuffer[readSourceLocation], length);
@ -183,7 +186,7 @@ bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, siz
} else {
readTargetLocation += readOffset >> 1;
}
if (readTargetLocation < 0 || readTargetLocation > outSize) {
if (readTargetLocation < 0 || readTargetLocation > (ssize_t) outSize) {
return false;
}
for (i = 0; i < length; ++i) {

View File

@ -16,7 +16,7 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#define SOCKET_FAILED(s) (s) == INVALID_SOCKET
#define SOCKET_FAILED(s) ((s) == INVALID_SOCKET)
typedef SOCKET Socket;
#else
#include <fcntl.h>
@ -25,7 +25,7 @@ typedef SOCKET Socket;
#include <sys/socket.h>
#define INVALID_SOCKET (-1)
#define SOCKET_FAILED(s) (s) < 0
#define SOCKET_FAILED(s) ((s) < 0)
typedef int Socket;
#endif
@ -157,7 +157,7 @@ static inline int SocketClose(Socket socket) {
return close(socket) >= 0;
}
static inline int SocketSetBlocking(Socket socket, int blocking) {
static inline int SocketSetBlocking(Socket socket, bool blocking) {
#ifdef _WIN32
u_long unblocking = !blocking;
return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR;

View File

@ -76,7 +76,7 @@ class Suite(object):
def collect_tests(self):
roms = []
for f in os.listdir(self.cwd):
if f.endswith('.gba'):
if f.endswith('.gba') or f.endswith('.zip'):
roms.append(f)
roms.sort()
for rom in roms: