diff --git a/CMakeLists.txt b/CMakeLists.txt index 02d4d420f..923c716d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,13 @@ else() source_group("POSIX-specific code" FILES ${OS_SRC}) endif() +if(BUILD_BBB OR BUILD_RASPI) + enable_language(ASM) + if(NOT BUILD_EGL) + add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5) + endif() +endif() + set(DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/debugger.c;${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c") if(USE_CLI_DEBUGGER) diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 7ad1fe27d..b8759a12d 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -1,13 +1,8 @@ #include "gdb-stub.h" #include -#include -#include #include -#include #include -#include -#include enum GDBError { GDB_NO_ERROR = 0x00, @@ -50,15 +45,12 @@ static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReas static void _gdbStubPoll(struct ARMDebugger* debugger) { struct GDBStub* stub = (struct GDBStub*) debugger; - int flags; while (stub->d.state == DEBUGGER_PAUSED) { if (stub->connection >= 0) { - flags = fcntl(stub->connection, F_GETFL); - if (flags == -1) { + if (!SocketSetBlocking(stub->connection, 1)) { GDBStubHangup(stub); return; } - fcntl(stub->connection, F_SETFL, flags & ~O_NONBLOCK); } GDBStubUpdate(stub); } @@ -66,7 +58,7 @@ static void _gdbStubPoll(struct ARMDebugger* debugger) { static void _ack(struct GDBStub* stub) { char ack = '+'; - send(stub->connection, &ack, 1, 0); + SocketSend(stub->connection, &ack, 1); } static void _nak(struct GDBStub* stub) { @@ -74,7 +66,7 @@ static void _nak(struct GDBStub* stub) { if (stub->d.log) { stub->d.log(&stub->d, DEBUGGER_LOG_WARN, "Packet error"); } - send(stub->connection, &nak, 1, 0); + SocketSend(stub->connection, &nak, 1); } static uint32_t _hex2int(const char* hex, int maxDigits) { @@ -155,7 +147,7 @@ static void _sendMessage(struct GDBStub* stub) { if (stub->d.log) { stub->d.log(&stub->d, DEBUGGER_LOG_DEBUG, "> %s", stub->outgoing); } - send(stub->connection, stub->outgoing, i + 3, 0); + SocketSend(stub->connection, stub->outgoing, i + 3); } static void _error(struct GDBStub* stub, enum GDBError error) { @@ -171,12 +163,10 @@ static void _writeHostInfo(struct GDBStub* stub) { static void _continue(struct GDBStub* stub, const char* message) { stub->d.state = DEBUGGER_RUNNING; if (stub->connection >= 0) { - int flags = fcntl(stub->connection, F_GETFL); - if (flags == -1) { + if (!SocketSetBlocking(stub->connection, 0)) { GDBStubHangup(stub); return; } - fcntl(stub->connection, F_SETFL, flags | O_NONBLOCK); } // TODO: parse message (void) (message); @@ -445,34 +435,20 @@ int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) { GDBStubShutdown(stub); } // TODO: support IPv6 - stub->socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + stub->socket = SocketOpenTCP(port, bindAddress); if (stub->socket < 0) { if (stub->d.log) { stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket"); } return 0; } - - struct sockaddr_in bindInfo = { - .sin_family = AF_INET, - .sin_port = htons(port), - .sin_addr = { - .s_addr = htonl(bindAddress) - } - }; - int err = bind(stub->socket, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in)); + int err = SocketListen(stub->socket, 1); if (err) { goto cleanup; } - err = listen(stub->socket, 1); - if (err) { + if (!SocketSetBlocking(stub->socket, 0)) { goto cleanup; } - int flags = fcntl(stub->socket, F_GETFL); - if (flags == -1) { - goto cleanup; - } - fcntl(stub->socket, F_SETFL, flags | O_NONBLOCK); return 1; @@ -480,14 +456,14 @@ cleanup: if (stub->d.log) { stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port"); } - close(stub->socket); + SocketClose(stub->socket); stub->socket = -1; return 0; } void GDBStubHangup(struct GDBStub* stub) { if (stub->connection >= 0) { - close(stub->connection); + SocketClose(stub->connection); stub->connection = -1; } if (stub->d.state == DEBUGGER_PAUSED) { @@ -498,7 +474,7 @@ void GDBStubHangup(struct GDBStub* stub) { void GDBStubShutdown(struct GDBStub* stub) { GDBStubHangup(stub); if (stub->socket >= 0) { - close(stub->socket); + SocketClose(stub->socket); stub->socket = -1; } } @@ -508,13 +484,11 @@ void GDBStubUpdate(struct GDBStub* stub) { return; } if (stub->connection == -1) { - stub->connection = accept(stub->socket, 0, 0); + stub->connection = SocketAccept(stub->socket, 0, 0); if (stub->connection >= 0) { - int flags = fcntl(stub->connection, F_GETFL); - if (flags == -1) { + if (!SocketSetBlocking(stub->connection, 0)) { goto connectionLost; } - fcntl(stub->connection, F_SETFL, flags | O_NONBLOCK); ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED); } else if (errno == EWOULDBLOCK || errno == EAGAIN) { return; @@ -523,7 +497,7 @@ void GDBStubUpdate(struct GDBStub* stub) { } } while (1) { - ssize_t messageLen = recv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1, 0); + ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1); if (messageLen == 0) { goto connectionLost; } diff --git a/src/debugger/gdb-stub.h b/src/debugger/gdb-stub.h index 5be0fd0ad..b60540edf 100644 --- a/src/debugger/gdb-stub.h +++ b/src/debugger/gdb-stub.h @@ -2,6 +2,7 @@ #define GDB_STUB_H #include "debugger.h" +#include "socket.h" #define GDB_STUB_MAX_LINE 1200 @@ -19,8 +20,8 @@ struct GDBStub { char outgoing[GDB_STUB_MAX_LINE]; enum GDBStubAckState lineAck; - int socket; - int connection; + Socket socket; + Socket connection; }; void GDBStubCreate(struct GDBStub*); diff --git a/src/gba/gba-io.c b/src/gba/gba-io.c index ec2381b8d..3d5b9bdf4 100644 --- a/src/gba/gba-io.c +++ b/src/gba/gba-io.c @@ -1,6 +1,7 @@ #include "gba-io.h" #include "gba-serialize.h" +#include "gba-sio.h" #include "gba-video.h" static const int _isValidRegister[REG_MAX >> 1] = { @@ -89,7 +90,7 @@ static const int _isSpecialRegister[REG_MAX >> 1] = { void GBAIOInit(struct GBA* gba) { gba->memory.io[REG_DISPCNT >> 1] = 0x0080; - gba->memory.io[REG_RCNT >> 1] = 0x8000; + gba->memory.io[REG_RCNT >> 1] = RCNT_INITIAL; gba->memory.io[REG_KEYINPUT >> 1] = 0x3FF; gba->memory.io[REG_SOUNDBIAS >> 1] = 0x200; } @@ -254,6 +255,18 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { GBATimerWriteTMCNT_HI(gba, 3, value); break; + // SIO + case REG_SIOCNT: + GBASIOWriteSIOCNT(&gba->sio, value); + break; + case REG_RCNT: + value &= 0xC1FF; + GBASIOWriteRCNT(&gba->sio, value); + break; + case REG_SIOMLT_SEND: + GBASIOWriteSIOMLT_SEND(&gba->sio, value); + break; + // Interrupts and misc case REG_WAITCNT: GBAAdjustWaitstates(&gba->memory, value); @@ -369,6 +382,11 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { } break; + case REG_SIOCNT: + return gba->sio.siocnt; + case REG_RCNT: + return gba->sio.rcnt; + case REG_DMA0CNT_LO: case REG_DMA1CNT_LO: case REG_DMA2CNT_LO: @@ -400,6 +418,11 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { case REG_DMA1CNT_HI: case REG_DMA2CNT_HI: case REG_DMA3CNT_HI: + case REG_SIOMULTI0: + case REG_SIOMULTI1: + case REG_SIOMULTI2: + case REG_SIOMULTI3: + case REG_SIOMLT_SEND: case REG_IE: case REG_IF: case REG_WAITCNT: diff --git a/src/gba/gba-sio.c b/src/gba/gba-sio.c new file mode 100644 index 000000000..0da1f8085 --- /dev/null +++ b/src/gba/gba-sio.c @@ -0,0 +1,139 @@ +#include "gba-sio.h" + +#include "gba-io.h" + +#include + +static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) { + switch (mode) { + case SIO_NORMAL_8: + case SIO_NORMAL_32: + return sio->drivers.normal; + case SIO_MULTI: + return sio->drivers.multiplayer; + case SIO_JOYBUS: + return sio->drivers.joybus; + default: + return 0; + } +} + +static void _switchMode(struct GBASIO* sio) { + int mode = ((sio->rcnt >> 14) & 0xC) | ((sio->siocnt >> 12) & 0x3); + enum GBASIOMode oldMode = sio->mode; + if (mode < 8) { + sio->mode = (enum GBASIOMode) (mode & 0x3); + } else { + sio->mode = (enum GBASIOMode) (mode & 0xC); + } + if (oldMode != mode) { + if (sio->activeDriver && sio->activeDriver->unload) { + sio->activeDriver->unload(sio->activeDriver); + } + sio->activeDriver = _lookupDriver(sio, mode); + if (sio->activeDriver && sio->activeDriver->load) { + sio->activeDriver->load(sio->activeDriver); + } + } +} + +void GBASIOInit(struct GBASIO* sio) { + sio->rcnt = RCNT_INITIAL; + sio->siocnt = 0; + _switchMode(sio); +} + +void GBASIODeinit(struct GBASIO* sio) { + if (sio->drivers.multiplayer && sio->drivers.multiplayer->deinit) { + sio->drivers.multiplayer->deinit(sio->drivers.multiplayer); + } + if (sio->drivers.joybus && sio->drivers.joybus->deinit) { + sio->drivers.joybus->deinit(sio->drivers.joybus); + } +} + +void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) { + if (drivers->normal) { + GBASIOSetDriver(sio, drivers->normal, SIO_NORMAL_8); + } + if (drivers->multiplayer) { + GBASIOSetDriver(sio, drivers->multiplayer, SIO_MULTI); + } + if (drivers->joybus) { + GBASIOSetDriver(sio, drivers->multiplayer, SIO_JOYBUS); + } +} + +void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode) { + struct GBASIODriver** driverLoc; + switch (mode) { + case SIO_NORMAL_8: + case SIO_NORMAL_32: + driverLoc = &sio->drivers.normal; + break; + case SIO_MULTI: + driverLoc = &sio->drivers.multiplayer; + break; + case SIO_JOYBUS: + driverLoc = &sio->drivers.joybus; + break; + default: + GBALog(sio->p, GBA_LOG_ERROR, "Setting an unsupported SIO driver: %x", mode); + return; + } + if (*driverLoc) { + if ((*driverLoc)->unload) { + (*driverLoc)->unload(*driverLoc); + } + if ((*driverLoc)->deinit) { + (*driverLoc)->deinit(*driverLoc); + } + } + if (driver) { + driver->p = sio; + + if (driver->init) { + if (!driver->init(driver)) { + driver->deinit(driver); + GBALog(sio->p, GBA_LOG_ERROR, "Could not initialize SIO driver"); + return; + } + } + if (sio->mode == mode) { + sio->activeDriver = driver; + if (driver->load) { + driver->load(driver); + } + } + } + *driverLoc = driver; +} + +void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { + sio->rcnt = value; + _switchMode(sio); + if (sio->activeDriver && sio->activeDriver->writeRegister) { + sio->activeDriver->writeRegister(sio->activeDriver, REG_RCNT, value); + } +} + +void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { + if (sio->activeDriver && sio->activeDriver->writeRegister) { + value = sio->activeDriver->writeRegister(sio->activeDriver, REG_SIOCNT, value); + } + sio->siocnt = value; + _switchMode(sio); +} + +void GBASIOWriteSIOMLT_SEND(struct GBASIO* sio, uint16_t value) { + if (sio->activeDriver && sio->activeDriver->writeRegister) { + sio->activeDriver->writeRegister(sio->activeDriver, REG_SIOMLT_SEND, value); + } +} + +int32_t GBASIOProcessEvents(struct GBASIO* sio, int32_t cycles) { + if (sio->activeDriver && sio->activeDriver->processEvents) { + return sio->activeDriver->processEvents(sio->activeDriver, cycles); + } + return INT_MAX; +} diff --git a/src/gba/gba-sio.h b/src/gba/gba-sio.h new file mode 100644 index 000000000..7a8f57c49 --- /dev/null +++ b/src/gba/gba-sio.h @@ -0,0 +1,89 @@ +#ifndef GBA_SIO_H +#define GBA_SIO_H + +#include + +enum GBASIOMode { + SIO_NORMAL_8 = 0, + SIO_NORMAL_32 = 1, + SIO_MULTI = 2, + SIO_UART = 3, + SIO_GPIO = 8, + SIO_JOYBUS = 12 +}; + +enum { + RCNT_INITIAL = 0x8000 +}; + +struct GBASIO; + +struct GBASIODriver { + struct GBASIO* p; + + int (*init)(struct GBASIODriver* driver); + void (*deinit)(struct GBASIODriver* driver); + int (*load)(struct GBASIODriver* driver); + int (*unload)(struct GBASIODriver* driver); + int (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); + int32_t (*processEvents)(struct GBASIODriver* driver, int32_t cycles); +}; + +struct GBASIODriverSet { + struct GBASIODriver* normal; + struct GBASIODriver* multiplayer; + struct GBASIODriver* joybus; +}; + +struct GBASIO { + struct GBA* p; + + enum GBASIOMode mode; + struct GBASIODriverSet drivers; + struct GBASIODriver* activeDriver; + + uint16_t rcnt; + union { + struct { + unsigned sc : 1; + unsigned internalSc : 1; + unsigned si : 1; + unsigned idleSo : 1; + unsigned : 4; + unsigned start : 1; + unsigned : 3; + unsigned length : 1; + unsigned : 1; + unsigned irq : 1; + unsigned : 1; + } normalControl; + + struct { + unsigned baud : 2; + unsigned slave : 1; + unsigned ready : 1; + unsigned id : 2; + unsigned error : 1; + unsigned busy : 1; + unsigned : 6; + unsigned irq : 1; + unsigned : 1; + } multiplayerControl; + + uint16_t siocnt; + }; +}; + +void GBASIOInit(struct GBASIO* sio); +void GBASIODeinit(struct GBASIO* sio); + +void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers); +void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode); + +void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value); +void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value); +void GBASIOWriteSIOMLT_SEND(struct GBASIO* sio, uint16_t value); + +int32_t GBASIOProcessEvents(struct GBASIO* sio, int32_t cycles); + +#endif diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 5b0f88af3..56a5555bd 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -37,6 +37,12 @@ static void _changeState(struct GBAThread* threadContext, enum ThreadState newSt MutexUnlock(&threadContext->stateMutex); } +static void _waitOnInterrupt(struct GBAThread* threadContext) { + while (threadContext->state == THREAD_INTERRUPTED) { + ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); + } +} + static THREAD_ENTRY _GBAThreadRun(void* context) { #ifdef USE_PTHREADS pthread_once(&_contextOnce, _createTLS); @@ -99,6 +105,8 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { ARMDebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED); } + GBASIOSetDriverSet(&gba.sio, &threadContext->sioDrivers); + gba.keySource = &threadContext->activeKeys; if (threadContext->startCallback) { @@ -120,7 +128,7 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { } } MutexLock(&threadContext->stateMutex); - while (threadContext->state == THREAD_PAUSED) { + while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) { ConditionWait(&threadContext->stateCond, &threadContext->stateMutex); } MutexUnlock(&threadContext->stateMutex); @@ -237,9 +245,25 @@ void GBAThreadJoin(struct GBAThread* threadContext) { free(threadContext->rewindBuffer); } +void GBAThreadInterrupt(struct GBAThread* threadContext) { + MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); + threadContext->savedState = threadContext->state; + threadContext->state = THREAD_INTERRUPTED; + if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { + threadContext->debugger->state = DEBUGGER_EXITING; + } + MutexUnlock(&threadContext->stateMutex); +} + +void GBAThreadContinue(struct GBAThread* threadContext) { + _changeState(threadContext, threadContext->savedState, 1); +} + void GBAThreadPause(struct GBAThread* threadContext) { int frameOn = 1; MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); if (threadContext->state == THREAD_RUNNING) { if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) { threadContext->debugger->state = DEBUGGER_EXITING; @@ -259,6 +283,7 @@ void GBAThreadPause(struct GBAThread* threadContext) { void GBAThreadUnpause(struct GBAThread* threadContext) { int frameOn = 1; MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); if (threadContext->state == THREAD_PAUSED) { threadContext->state = THREAD_RUNNING; ConditionWake(&threadContext->stateCond); @@ -275,6 +300,7 @@ void GBAThreadUnpause(struct GBAThread* threadContext) { int GBAThreadIsPaused(struct GBAThread* threadContext) { int isPaused; MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); isPaused = threadContext->state == THREAD_PAUSED; MutexUnlock(&threadContext->stateMutex); return isPaused; @@ -283,6 +309,7 @@ int GBAThreadIsPaused(struct GBAThread* threadContext) { void GBAThreadTogglePause(struct GBAThread* threadContext) { int frameOn = 1; MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); if (threadContext->state == THREAD_PAUSED) { threadContext->state = THREAD_RUNNING; ConditionWake(&threadContext->stateCond); diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index 582d4990f..b19432f3c 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -10,9 +10,10 @@ typedef void (*ThreadCallback)(struct GBAThread* threadContext); enum ThreadState { THREAD_INITIALIZED = -1, THREAD_RUNNING = 0, - THREAD_PAUSED = 1, - THREAD_EXITING = 2, - THREAD_SHUTDOWN = 3 + THREAD_INTERRUPTED = 1, + THREAD_PAUSED = 2, + THREAD_EXITING = 3, + THREAD_SHUTDOWN = 4 }; struct GBASync { @@ -36,6 +37,7 @@ struct GBAThread { // Input struct GBAVideoRenderer* renderer; + struct GBASIODriverSet sioDrivers; struct ARMDebugger* debugger; int fd; int biosFd; @@ -48,6 +50,7 @@ struct GBAThread { Mutex stateMutex; Condition stateCond; + enum ThreadState savedState; GBALogHandler logHandler; ThreadCallback startCallback; @@ -70,6 +73,9 @@ int GBAThreadHasStarted(struct GBAThread* threadContext); void GBAThreadEnd(struct GBAThread* threadContext); void GBAThreadJoin(struct GBAThread* threadContext); +void GBAThreadInterrupt(struct GBAThread* threadContext); +void GBAThreadContinue(struct GBAThread* threadContext); + void GBAThreadPause(struct GBAThread* threadContext); void GBAThreadUnpause(struct GBAThread* threadContext); int GBAThreadIsPaused(struct GBAThread* threadContext); diff --git a/src/gba/gba.c b/src/gba/gba.c index c0b8223ae..4550b53c9 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -2,6 +2,7 @@ #include "gba-bios.h" #include "gba-io.h" +#include "gba-sio.h" #include "gba-thread.h" #include "memory.h" @@ -122,6 +123,9 @@ void GBAInit(struct GBA* gba) { GBAIOInit(gba); + gba->sio.p = gba; + GBASIOInit(&gba->sio); + gba->timersEnabled = 0; memset(gba->timers, 0, sizeof(gba->timers)); @@ -195,6 +199,11 @@ static void GBAProcessEvents(struct ARMBoard* board) { nextEvent = testEvent; } + testEvent = GBASIOProcessEvents(&gbaBoard->p->sio, cycles); + if (testEvent < nextEvent) { + nextEvent = testEvent; + } + board->cpu->cycles -= cycles; board->cpu->nextEvent = nextEvent; } while (board->cpu->cycles >= board->cpu->nextEvent); @@ -444,10 +453,6 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { }; void GBAWriteIE(struct GBA* gba, uint16_t value) { - if (value & (1 << IRQ_SIO)) { - GBALog(gba, GBA_LOG_STUB, "SIO interrupts not implemented"); - } - if (value & (1 << IRQ_KEYPAD)) { GBALog(gba, GBA_LOG_STUB, "Keypad interrupts not implemented"); } diff --git a/src/gba/gba.h b/src/gba/gba.h index 8b55ccd5d..0b33f5cae 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -7,6 +7,7 @@ #include "gba-memory.h" #include "gba-video.h" #include "gba-audio.h" +#include "gba-sio.h" #include @@ -74,6 +75,7 @@ struct GBA { struct GBAMemory memory; struct GBAVideo video; struct GBAAudio audio; + struct GBASIO sio; struct GBASync* sync; diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 7745464d6..e95bef330 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -1,4 +1,19 @@ -find_package(SDL 1.2 REQUIRED) +set(SDL_VERSION "2" CACHE STRING "Version of SDL to use (1.2 or 2)") + +if (SDL_VERSION EQUAL "2") + include(FindPkgConfig) + pkg_search_module(SDL2 sdl2) + if (SDL2_FOUND) + set(SDL_INCLUDE_DIR ${SDL2_INCLUDE_DIRS}) + set(SDL_LIBRARY ${SDL2_LIBRARIES}) + set(SDLMAIN_LIBRARY "") + endif() +endif() + +if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND) + find_package(SDL 1.2 REQUIRED) +endif() + file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c) set(PLATFORM_LIBRARY "${SDL_LIBRARY};${SDLMAIN_LIBRARY}") include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl) @@ -9,8 +24,6 @@ if(BUILD_RASPI AND BUILD_EGL) set(OPENGL_INCLUDE_DIR "") add_definitions(-DBUILD_RASPI) elseif(BUILD_BBB OR BUILD_RASPI) - enable_language(ASM) - add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5) set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-main.c) else() set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/gl-main.c) diff --git a/src/platform/sdl/gl-main.c b/src/platform/sdl/gl-main.c index 56039614f..bce26894f 100644 --- a/src/platform/sdl/gl-main.c +++ b/src/platform/sdl/gl-main.c @@ -22,6 +22,9 @@ struct GLSoftwareRenderer { struct GBAVideoSoftwareRenderer d; struct GBASDLAudio audio; struct GBASDLEvents events; +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Window* window; +#endif int viewportWidth; int viewportHeight; @@ -105,15 +108,38 @@ static int _GBASDLInit(struct GLSoftwareRenderer* renderer) { GBASDLInitEvents(&renderer->events); GBASDLInitAudio(&renderer->audio); +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +#else SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); +#endif + +#ifndef COLOR_16_BIT SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); +#else + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); +#ifdef COLOR_5_6_5 + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); +#else + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); +#endif + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); +#endif + +#if SDL_VERSION_ATLEAST(2, 0, 0) + renderer->window = SDL_CreateWindow("GBAc", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL); + SDL_GL_CreateContext(renderer->window); + SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); + renderer->events.window = renderer->window; + renderer->events.fullscreen = 0; +#else #ifdef COLOR_16_BIT SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL); #else SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL); +#endif #endif renderer->d.outputBuffer = malloc(256 * 256 * 4); @@ -158,10 +184,20 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* } } GBASyncWaitFrameEnd(&context->sync); +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_GL_SwapWindow(renderer->window); +#else SDL_GL_SwapBuffers(); +#endif while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &event); + int fullscreen = renderer->events.fullscreen; + GBASDLHandleEvent(context, &renderer->events, &event); + // Event handling can change the size of the screen + if (renderer->events.fullscreen != fullscreen) { + SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); + glViewport(0, 0, renderer->viewportWidth, renderer->viewportHeight); + } } } } @@ -171,6 +207,9 @@ static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) { GBASDLDeinitEvents(&renderer->events); GBASDLDeinitAudio(&renderer->audio); +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_DestroyWindow(renderer->window); +#endif SDL_Quit(); } diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index c127f720e..af20fc3ec 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -5,13 +5,21 @@ #include "gba-serialize.h" #include "gba-video.h" +#if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__) +#define GUI_MOD KMOD_GUI +#else +#define GUI_MOD KMOD_CTRL +#endif + int GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { return 0; } SDL_JoystickEventState(SDL_ENABLE); context->joystick = SDL_JoystickOpen(0); +#if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); +#endif return 1; } @@ -45,9 +53,8 @@ static void _pauseAfterFrame(struct GBAThread* context) { GBAThreadPause(context); } -static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event) { +static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_KeyboardEvent* event) { enum GBAKey key = 0; - int isPaused = GBAThreadIsPaused(context); switch (event->keysym.sym) { case SDLK_z: key = GBA_KEY_A; @@ -83,22 +90,25 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke if (event->type == SDL_KEYDOWN && context->debugger) { ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL); } - break; + return; case SDLK_TAB: context->sync.audioWait = event->type != SDL_KEYDOWN; return; case SDLK_LEFTBRACKET: - if (!isPaused) { - GBAThreadPause(context); - } + GBAThreadInterrupt(context); GBARewind(context, 10); - if (!isPaused) { - GBAThreadUnpause(context); - } + GBAThreadContinue(context); + return; default: if (event->type == SDL_KEYDOWN) { - if (event->keysym.mod & KMOD_CTRL) { + if (event->keysym.mod & GUI_MOD) { switch (event->keysym.sym) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + case SDLK_f: + SDL_SetWindowFullscreen(sdlContext->window, sdlContext->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP); + sdlContext->fullscreen = !sdlContext->fullscreen; + break; +#endif case SDLK_p: GBAThreadTogglePause(context); break; @@ -123,13 +133,9 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke case SDLK_F8: case SDLK_F9: case SDLK_F10: - if (!isPaused) { - GBAThreadPause(context); - } + GBAThreadInterrupt(context); GBASaveState(context->gba, event->keysym.sym - SDLK_F1); - if (!isPaused) { - GBAThreadUnpause(context); - } + GBAThreadContinue(context); break; default: break; @@ -146,13 +152,9 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke case SDLK_F8: case SDLK_F9: case SDLK_F10: - if (!isPaused) { - GBAThreadPause(context); - } + GBAThreadInterrupt(context); GBALoadState(context->gba, event->keysym.sym - SDLK_F1); - if (!isPaused) { - GBAThreadUnpause(context); - } + GBAThreadContinue(context); break; default: break; @@ -203,7 +205,7 @@ static void _GBASDLHandleJoyHat(struct GBAThread* context, const struct SDL_JoyH context->activeKeys |= key; } -void GBASDLHandleEvent(struct GBAThread* context, const union SDL_Event* event) { +void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event) { switch (event->type) { case SDL_QUIT: // FIXME: this isn't thread-safe @@ -217,7 +219,7 @@ void GBASDLHandleEvent(struct GBAThread* context, const union SDL_Event* event) break; case SDL_KEYDOWN: case SDL_KEYUP: - _GBASDLHandleKeypress(context, &event->key); + _GBASDLHandleKeypress(context, sdlContext, &event->key); break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index f9592d308..437a3a4a4 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -7,12 +7,18 @@ struct GBASDLEvents { SDL_Joystick* joystick; +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Window* window; + int fullscreen; +#endif }; int GBASDLInitEvents(struct GBASDLEvents*); void GBASDLDeinitEvents(struct GBASDLEvents*); -void GBASDLHandleEvent(struct GBAThread* context, const union SDL_Event* event); +void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event); + +enum GBAKey GBASDLMapButtonToKey(int button); enum GBAKey GBASDLMapButtonToKey(int button); diff --git a/src/util/socket.h b/src/util/socket.h new file mode 100644 index 000000000..433e9bd8d --- /dev/null +++ b/src/util/socket.h @@ -0,0 +1,89 @@ +#ifndef SOCKET_H +#define SOCKET_H + +#ifdef _WIN32 +#include + +typedef SOCKET Socket; +#else +#include +#include +#include +#include +#include +#include + +typedef int Socket; +#endif + + +static inline void SocketSubsystemInitialize() { +#ifdef _WIN32 + WSAStartup(MAKEWORD(2, 2), 0); +#endif +} + +static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) { + return write(socket, buffer, size); +} + +static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) { + return read(socket, buffer, size); +} + +static inline Socket SocketOpenTCP(int port, uint32_t bindAddress) { + Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) { + return sock; + } + + struct sockaddr_in bindInfo = { + .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr = { + .s_addr = htonl(bindAddress) + } + }; + int err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in)); + if (err) { + close(sock); + return -1; + } + return sock; +} + +static inline Socket SocketListen(Socket socket, int queueLength) { + return listen(socket, queueLength); +} + +static inline Socket SocketAccept(Socket socket, struct sockaddr* restrict address, socklen_t* restrict addressLength) { + return accept(socket, address, addressLength); +} + +static inline int SocketClose(Socket socket) { + return close(socket) >= 0; +} + +static inline int SocketSetBlocking(Socket socket, int blocking) { +#ifdef _WIN32 + blocking = !blocking; + return ioctlsocket(socket, FIONBIO, &blocking) == NO_ERROR; +#else + int flags = fcntl(socket, F_GETFL); + if (flags == -1) { + return 0; + } + if (blocking) { + flags &= ~O_NONBLOCK; + } else { + flags |= O_NONBLOCK; + } + return fcntl(socket, F_SETFL, flags) >= 0; +#endif +} + +static inline int SocketSetTCPPush(Socket socket, int push) { + return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0; +} + +#endif