all repos — mgba @ d49a9a84f782236137966f8c83934e77db5c067e

mGBA Game Boy Advance Emulator

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

Conflicts:
	CHANGES
	src/gba/gba-overrides.c
	src/gba/gba-thread.h
	src/gba/gba.c
Jeffrey Pfau jeffrey@endrift.com
Fri, 16 Jan 2015 02:55:21 -0800
commit

d49a9a84f782236137966f8c83934e77db5c067e

parent

eb81fc3c65f918c16f0e9ddeeb6cbea662155731

M CHANGESCHANGES

@@ -19,6 +19,7 @@ - Support IPv6

- 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 @@ - Qt: Fix some cases where key mapping can break if focus is adjusted

- 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 @@ - Qt: Clarify some phrasing in the menus

- 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
M src/debugger/gdb-stub.csrc/debugger/gdb-stub.c

@@ -53,15 +53,39 @@ }

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; - } + --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; } - GDBStubUpdate(stub); + 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 @@ _sendMessage(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 @@ stub->socket = INVALID_SOCKET;

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) {

@@ -459,9 +486,6 @@ return 0;

} int err = SocketListen(stub->socket, 1); if (err) { - goto cleanup; - } - if (!SocketSetBlocking(stub->socket, 0)) { goto cleanup; }

@@ -501,9 +525,6 @@ }

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;
M src/debugger/gdb-stub.hsrc/debugger/gdb-stub.h

@@ -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 @@ enum GDBStubAckState lineAck;

Socket socket; Socket connection; + + bool shouldBlock; + int untilPoll; }; void GDBStubCreate(struct GDBStub*);
M src/debugger/memory-debugger.csrc/debugger/memory-debugger.c

@@ -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 @@ } \

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 @@ debugger->cpu->memory.store8 = ARMDebuggerShim_store8;

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 @@ debugger->cpu->memory.store8 = debugger->originalMemory.store8;

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; }
M src/gba/gba-audio.csrc/gba/gba-audio.c

@@ -53,8 +53,8 @@ audio->ch1 = (struct GBAAudioChannel1) { .envelope = { .nextStep = INT_MAX }, .nextSweep = INT_MAX };

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;
M src/gba/gba-bios.csrc/gba/gba-bios.c

@@ -215,7 +215,10 @@ case 0xC:

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 @@ break;

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 @@ uint32_t dest = cpu->gprs[1];

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;

@@ -440,9 +446,6 @@ block = 0;

} } - } - if (padding) { - cpu->memory.store32(cpu, dest, block, 0); } cpu->gprs[0] = source; cpu->gprs[1] = dest;
M src/gba/gba-memory.csrc/gba/gba-memory.c

@@ -101,9 +101,10 @@ memset(gba->memory.io, 0, sizeof(gba->memory.io));

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 @@ LOAD_32(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); \

++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 @@ if (memory->activeRegion == REGION_BIOS) {

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 @@ case REGION_PALETTE_RAM:

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 @@ break;

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 @@ if (memory->activeRegion == REGION_BIOS) {

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 @@ case REGION_PALETTE_RAM:

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 @@ break;

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 @@ ++wait; \

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 @@ STORE_16(value, address & (SIZE_PALETTE_RAM - 1), gba->video.palette);

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;
M src/gba/gba-serialize.csrc/gba/gba-serialize.c

@@ -40,6 +40,10 @@ state->cpu.nextEvent = gba->cpu->nextEvent;

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 @@ memcpy(gba->cpu->bankedRegisters, state->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t));

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);
M src/gba/gba-serialize.hsrc/gba/gba-serialize.h

@@ -150,7 +150,11 @@ * | 0x002C0 - 0x002C0: Light sample

* | 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 @@ unsigned tiltState : 2;

unsigned : 22; } gpio; - uint32_t reservedGpio[15]; + uint32_t reservedGpio[12]; + + uint32_t biosPrefetch; + uint32_t cpuPrefetch[2]; uint32_t associatedStreamId;
M src/gba/gba-video.csrc/gba/gba-video.c

@@ -42,7 +42,7 @@ }

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;
M src/gba/gba.csrc/gba/gba.c

@@ -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 @@

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;
M src/gba/hle-bios.csrc/gba/hle-bios.c

@@ -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 };
M src/gba/hle-bios.ssrc/gba/hle-bios.s

@@ -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
M src/gba/renderers/video-software.csrc/gba/renderers/video-software.c

@@ -1010,6 +1010,10 @@ BACKGROUND_TEXT_SELECT_CHARACTER; \

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 @@ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \

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) { \ + 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; \ + } \ + } \ + \ + if (LIKELY(charBase < 0x10000)) { \ LOAD_32(tileData, charBase, vram); \ - for (; outX >= end2; --outX, --pixel) { \ + for (; outX >= renderer->start; --outX, --pixel) { \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ } \ - charBase += 4; \ - } \ - \ - 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 @@ }

#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 @@ #define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \

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) { \
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -112,46 +112,43 @@ context->activeKeys &= ~(1 << key);

} 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 @@ default:

break; } } + return; } - return; } }
M src/util/patch-ups.csrc/util/patch-ups.c

@@ -129,6 +129,9 @@ _decodeLength(patch->vf); // Discard input size

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 @@ writeLocation += length;

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 @@ readSourceLocation -= readOffset >> 1;

} 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 @@ readTargetLocation -= readOffset >> 1;

} else { readTargetLocation += readOffset >> 1; } - if (readTargetLocation < 0 || readTargetLocation > outSize) { + if (readTargetLocation < 0 || readTargetLocation > (ssize_t) outSize) { return false; } for (i = 0; i < length; ++i) {
M src/util/socket.hsrc/util/socket.h

@@ -16,7 +16,7 @@ #ifdef _WIN32

#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 @@ #include <netinet/tcp.h>

#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;
M tools/perf.pytools/perf.py

@@ -76,7 +76,7 @@

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: