From ff542cbb07d1ff7e34023c681a178f0d790b675b Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Sat, 10 Aug 2024 11:32:08 +1000 Subject: [PATCH] Rebase branch bizhawk-0.11 --- bizhawk-make-install.sh | 11 + src/platform/bizhawk/.gitignore | 2 + src/platform/bizhawk/base.mak | 130 ++++++ src/platform/bizhawk/bizinterface.c | 623 ++++++++++++++++++++++++++++ src/platform/bizhawk/linux/Makefile | 30 ++ src/platform/bizhawk/localtime_r.c | 179 ++++++++ src/platform/bizhawk/mingw/Makefile | 31 ++ 7 files changed, 1006 insertions(+) create mode 100755 bizhawk-make-install.sh create mode 100644 src/platform/bizhawk/.gitignore create mode 100644 src/platform/bizhawk/base.mak create mode 100644 src/platform/bizhawk/bizinterface.c create mode 100644 src/platform/bizhawk/linux/Makefile create mode 100644 src/platform/bizhawk/localtime_r.c create mode 100644 src/platform/bizhawk/mingw/Makefile diff --git a/bizhawk-make-install.sh b/bizhawk-make-install.sh new file mode 100755 index 00000000000..3638bc91f19 --- /dev/null +++ b/bizhawk-make-install.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +BASEDIR=$(dirname $0) +if [ "$(uname -s)" = "Linux" ]; then + cd "$BASEDIR/src/platform/bizhawk/linux" +else + cd "$BASEDIR/src/platform/bizhawk/mingw" +fi +make clean +make -j4 +make install diff --git a/src/platform/bizhawk/.gitignore b/src/platform/bizhawk/.gitignore new file mode 100644 index 00000000000..2179e3a19e0 --- /dev/null +++ b/src/platform/bizhawk/.gitignore @@ -0,0 +1,2 @@ +codeblocks/* +mingw/obj/* \ No newline at end of file diff --git a/src/platform/bizhawk/base.mak b/src/platform/bizhawk/base.mak new file mode 100644 index 00000000000..39bc815dc6b --- /dev/null +++ b/src/platform/bizhawk/base.mak @@ -0,0 +1,130 @@ +CCFLAGS = \ + -O3 -fomit-frame-pointer -fexpensive-optimizations -flto \ + -I../../../../include -I../../.. -std=gnu11 \ + -Wall -Wno-stringop-overflow -Wno-lto-type-mismatch \ + -Wno-ignored-optimization-argument -Wno-unknown-warning-option \ + -DMINIMAL_CORE=2 -DDISABLE_THREADING -DM_CORE_GBA \ + -DCOLOR_16_BIT -DMGBA_STANDALONE -DENABLE_DEBUGGERS \ + $(PLAT_FLAGS) + +DEST_64 = ../../../../../../Assets/dll +DESTCOPY_64 = ../../../../../../output/dll + +SRCS = \ + $(ROOT_DIR)/core/bitmap-cache.c \ + $(ROOT_DIR)/core/cache-set.c \ + $(ROOT_DIR)/core/cheats.c \ + $(ROOT_DIR)/core/config.c \ + $(ROOT_DIR)/core/core.c \ + $(ROOT_DIR)/core/directories.c \ + $(ROOT_DIR)/core/input.c \ + $(ROOT_DIR)/core/interface.c \ + $(ROOT_DIR)/core/library.c \ + $(ROOT_DIR)/core/lockstep.c \ + $(ROOT_DIR)/core/log.c \ + $(ROOT_DIR)/core/map-cache.c \ + $(ROOT_DIR)/core/mem-search.c \ + $(ROOT_DIR)/core/rewind.c \ + $(ROOT_DIR)/core/serialize.c \ + $(ROOT_DIR)/core/sync.c \ + $(ROOT_DIR)/core/thread.c \ + $(ROOT_DIR)/core/tile-cache.c \ + $(ROOT_DIR)/core/timing.c \ + $(ROOT_DIR)/arm/arm.c \ + $(ROOT_DIR)/arm/decoder-arm.c \ + $(ROOT_DIR)/arm/decoder-thumb.c \ + $(ROOT_DIR)/arm/decoder.c \ + $(ROOT_DIR)/arm/isa-arm.c \ + $(ROOT_DIR)/arm/isa-thumb.c \ + $(ROOT_DIR)/arm/debugger/cli-debugger.c \ + $(ROOT_DIR)/arm/debugger/memory-debugger.c \ + $(ROOT_DIR)/arm/debugger/debugger.c \ + $(ROOT_DIR)/gb/audio.c \ + $(ROOT_DIR)/gba/audio.c \ + $(ROOT_DIR)/gba/bios.c \ + $(ROOT_DIR)/gba/cheats.c \ + $(ROOT_DIR)/gba/core.c \ + $(ROOT_DIR)/gba/dma.c \ + $(ROOT_DIR)/gba/gba.c \ + $(ROOT_DIR)/gba/hle-bios.c \ + $(ROOT_DIR)/gba/input.c \ + $(ROOT_DIR)/gba/io.c \ + $(ROOT_DIR)/gba/memory.c \ + $(ROOT_DIR)/gba/overrides.c \ + $(ROOT_DIR)/gba/savedata.c \ + $(ROOT_DIR)/gba/serialize.c \ + $(ROOT_DIR)/gba/sharkport.c \ + $(ROOT_DIR)/gba/sio.c \ + $(ROOT_DIR)/gba/timer.c \ + $(ROOT_DIR)/gba/video.c \ + $(ROOT_DIR)/gba/cart/ereader.c \ + $(ROOT_DIR)/gba/cart/gpio.c \ + $(ROOT_DIR)/gba/cart/matrix.c \ + $(ROOT_DIR)/gba/cart/vfame.c \ + $(ROOT_DIR)/gba/cheats/codebreaker.c \ + $(ROOT_DIR)/gba/cheats/gameshark.c \ + $(ROOT_DIR)/gba/cheats/parv3.c \ + $(ROOT_DIR)/gba/debugger/cli.c \ + $(ROOT_DIR)/gba/renderers/cache-set.c \ + $(ROOT_DIR)/gba/renderers/common.c \ + $(ROOT_DIR)/gba/renderers/software-bg.c \ + $(ROOT_DIR)/gba/renderers/software-mode0.c \ + $(ROOT_DIR)/gba/renderers/software-obj.c \ + $(ROOT_DIR)/gba/renderers/video-software.c \ + $(ROOT_DIR)/gba/sio/gbp.c \ + $(ROOT_DIR)/gba/sio/joybus.c \ + $(ROOT_DIR)/gba/sio/lockstep.c \ + $(ROOT_DIR)/debugger/debugger.c \ + $(ROOT_DIR)/debugger/stack-trace.c \ + $(ROOT_DIR)/debugger/cli-debugger.c \ + $(ROOT_DIR)/debugger/symbols.c \ + $(ROOT_DIR)/debugger/parser.c \ + $(ROOT_DIR)/third-party/inih/ini.c \ + $(ROOT_DIR)/util/circle-buffer.c \ + $(ROOT_DIR)/util/configuration.c \ + $(ROOT_DIR)/util/crc32.c \ + $(ROOT_DIR)/util/elf-read.c \ + $(ROOT_DIR)/util/formatting.c \ + $(ROOT_DIR)/util/gbk-table.c \ + $(ROOT_DIR)/util/gui.c \ + $(ROOT_DIR)/util/hash.c \ + $(ROOT_DIR)/util/image/png-io.c \ + $(ROOT_DIR)/util/patch-fast.c \ + $(ROOT_DIR)/util/patch-ips.c \ + $(ROOT_DIR)/util/patch-ups.c \ + $(ROOT_DIR)/util/patch.c \ + $(ROOT_DIR)/util/ring-fifo.c \ + $(ROOT_DIR)/util/string.c \ + $(ROOT_DIR)/util/table.c \ + $(ROOT_DIR)/util/text-codec.c \ + $(ROOT_DIR)/util/vector.c \ + $(ROOT_DIR)/util/vfs.c \ + $(ROOT_DIR)/util/vfs/vfs-mem.c \ + $(ROOT_DIR)/platform/bizhawk/bizinterface.c \ + $(ROOT_DIR)/platform/bizhawk/localtime_r.c \ + $(PLAT_SRCS) + +_OBJS:=$(SRCS:.c=.o) +OBJS:=$(patsubst $(ROOT_DIR)%,$(OBJ_DIR)%,$(_OBJS)) + +all: $(TARGET) + +$(OBJ_DIR)/%.o: $(ROOT_DIR)/%.c + @mkdir -p $(@D) + @$(CC) -c -o $@ $< $(CCFLAGS) + +$(TARGET): $(OBJS) + @$(CC) -o $@ $(OBJS) $(LDFLAGS) + +clean: + @$(RM) -rf $(OBJ_DIR) + @$(RM) -f $(TARGET) + +install: $(TARGET) + $(CP) $(TARGET) $(DEST_$(ARCH)) +ifneq ("$(wildcard $(DESTCOPY_$(ARCH)))", "") + $(CP) $(TARGET) $(DESTCOPY_$(ARCH)) +endif + +print-%: + @echo $* = $($*) diff --git a/src/platform/bizhawk/bizinterface.c b/src/platform/bizhawk/bizinterface.c new file mode 100644 index 00000000000..933f277c7ae --- /dev/null +++ b/src/platform/bizhawk/bizinterface.c @@ -0,0 +1,623 @@ +#include +#include +#include "mgba/core/core.h" +#include "mgba/core/log.h" +#include "mgba/core/timing.h" +#include "mgba/core/serialize.h" +#include "mgba/core/blip_buf.h" +#include "mgba/gba/core.h" +#include "mgba/gba/interface.h" +#include "mgba/internal/gba/gba.h" +#include "mgba/internal/gba/video.h" +#include "mgba/internal/gba/overrides.h" +#include "mgba/internal/arm/isa-inlines.h" +#include "mgba/debugger/debugger.h" +#include "mgba-util/common.h" +#include "mgba-util/vfs.h" + +// interop sanity checks +static_assert(sizeof(bool) == 1, "Wrong bool size!"); +static_assert(sizeof(color_t) == 2, "Wrong color_t size!"); +static_assert(sizeof(enum mWatchpointType) == 4, "Wrong mWatchpointType size!"); +static_assert(sizeof(enum GBASavedataType) == 4, "Wrong GBASavedataType size!"); +static_assert(sizeof(enum GBAHardwareDevice) == 4, "Wrong GBAHardwareDevice size!"); +static_assert(sizeof(ssize_t) == 8, "Wrong ssize_t size!"); +static_assert(sizeof(void*) == 8, "Wrong pointer size!"); + +const char* const binaryName = "mgba"; +const char* const projectName = "mGBA BizHawk"; +const char* const projectVersion = "(unknown)"; + +#ifdef _WIN32 +#define EXP __declspec(dllexport) +#else +#define EXP __attribute__((visibility("default"))) +#endif + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +struct VFile* VFileOpenFD(const char* path, int flags) { return NULL; } + +typedef struct +{ + struct mCore* core; + struct mLogger logger; + struct GBA* gba; // anything that uses this will be deprecated eventually + color_t vbuff[GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS]; + void* rom; + struct VFile* romvf; + uint8_t bios[0x4000]; + struct VFile* biosvf; + uint8_t sram[0x20000 + 16]; + struct VFile* sramvf; + struct mKeyCallback keysource; + struct mRotationSource rotsource; + struct mRTCSource rtcsource; + struct GBALuminanceSource lumasource; + struct mDebugger debugger; + struct mDebuggerModule module; + bool attached; + struct GBACartridgeOverride override; + int16_t tiltx; + int16_t tilty; + int16_t tiltz; + int64_t time; + uint8_t light; + uint16_t keys; + bool lagged; + bool skipbios; + uint32_t palette[0x10000]; + void (*input_callback)(void); + void (*trace_callback)(const char *buffer); + void (*exec_callback)(uint32_t pc); + void (*mem_callback)(uint32_t addr, enum mWatchpointType type, uint32_t oldValue, uint32_t newValue); +} bizctx; + +static int32_t GetX(struct mRotationSource* rotationSource) +{ + return container_of(rotationSource, bizctx, rotsource)->tiltx << 16; +} +static int32_t GetY(struct mRotationSource* rotationSource) +{ + return container_of(rotationSource, bizctx, rotsource)->tilty << 16; +} +static int32_t GetZ(struct mRotationSource* rotationSource) +{ + return container_of(rotationSource, bizctx, rotsource)->tiltz << 16; +} +static uint8_t GetLight(struct GBALuminanceSource* luminanceSource) +{ + return container_of(luminanceSource, bizctx, lumasource)->light; +} +static time_t GetTime(struct mRTCSource* rtcSource) +{ + return container_of(rtcSource, bizctx, rtcsource)->time; +} +static uint16_t GetKeys(struct mKeyCallback* keypadSource) +{ + bizctx *ctx = container_of(keypadSource, bizctx, keysource); + ctx->input_callback(); + ctx->lagged = false; + return ctx->keys; +} +static void RotationCB(struct mRotationSource* rotationSource) +{ + bizctx* ctx = container_of(rotationSource, bizctx, rotsource); + ctx->input_callback(); + ctx->lagged = false; +} +static void LightCB(struct GBALuminanceSource* luminanceSource) +{ + bizctx* ctx = container_of(luminanceSource, bizctx, lumasource); + ctx->input_callback(); + ctx->lagged = false; +} +static void TimeCB(struct mRTCSource* rtcSource) +{ + // no, reading the rtc registers should not unset the lagged flag + // bizctx* ctx = container_of(rtcSource, bizctx, rtcsource); + // ctx->lagged = false; +} +static void logdebug(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) +{ + +} + +static void resetinternal(bizctx* ctx) +{ + // RTC will be reinit on GBAOverrideApply + // The RTC data contents in the sramvf are also read back + // This is done so RTC data is up to date + // note reset internally calls GBAOverrideApply potentially, so we do this now + GBASavedataRTCWrite(&ctx->gba->memory.savedata); + + ctx->core->reset(ctx->core); + if (ctx->skipbios) + GBASkipBIOS(ctx->gba); + + if (ctx->gba->memory.rom) + { + GBASavedataRTCWrite(&ctx->gba->memory.savedata); // again to be safe (not sure if needed?) + GBAOverrideApply(ctx->gba, &ctx->override); + } +} + +EXP void BizDestroy(bizctx* ctx) +{ + if (ctx->attached) + { + ctx->core->detachDebugger(ctx->core); + mDebuggerDetachModule(&ctx->debugger, &ctx->module); + mDebuggerDeinit(&ctx->debugger); + } + + ctx->core->deinit(ctx->core); + free(ctx->rom); + free(ctx); +} + +typedef struct +{ + enum GBASavedataType savetype; + enum GBAHardwareDevice hardware; + uint32_t idleLoop; + bool vbaBugCompat; + bool detectPokemonRomHacks; +} overrideinfo; + +void exec_hook(struct mDebuggerModule* module) +{ + bizctx* ctx = container_of(module, bizctx, module); + if (ctx->trace_callback) + { + char trace[1024]; + trace[sizeof(trace) - 1] = '\0'; + size_t traceSize = sizeof(trace) - 2; + ctx->debugger.platform->trace(ctx->debugger.platform, trace, &traceSize); + if (traceSize + 1 <= sizeof(trace)) { + trace[traceSize] = '\n'; + trace[traceSize + 1] = '\0'; + } + ctx->trace_callback(trace); + } + if (ctx->exec_callback) + ctx->exec_callback(_ARMPCAddress(ctx->core->cpu)); +} + +EXP void BizSetInputCallback(bizctx* ctx, void(*callback)(void)) +{ + ctx->input_callback = callback; +} + +EXP void BizSetTraceCallback(bizctx* ctx, void(*callback)(const char *buffer)) +{ + ctx->trace_callback = callback; +} + +EXP void BizSetExecCallback(bizctx* ctx, void(*callback)(uint32_t pc)) +{ + ctx->exec_callback = callback; +} + +EXP void BizSetMemCallback(bizctx* ctx, void(*callback)(uint32_t addr, enum mWatchpointType type, uint32_t oldValue, uint32_t newValue)) +{ + ctx->mem_callback = callback; +} + +static void watchpoint_entry(struct mDebuggerModule* module, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) +{ + bizctx* ctx = container_of(module, bizctx, module); + if (reason == DEBUGGER_ENTER_WATCHPOINT && info && ctx->mem_callback) + { + module->entered = NULL; // don't allow this callback to be re-entered + ctx->mem_callback(info->address, info->type.wp.accessType, info->type.wp.oldValue, info->type.wp.newValue); + module->entered = watchpoint_entry; + } + + module->isPaused = false; + module->needsCallback = ctx->trace_callback || ctx->exec_callback; +} + +EXP ssize_t BizSetWatchpoint(bizctx* ctx, uint32_t addr, enum mWatchpointType type) +{ + struct mWatchpoint watchpoint = { + .minAddress = addr, + .maxAddress = addr + 1, + .segment = -1, + .type = type + }; + return ctx->debugger.platform->setWatchpoint(ctx->debugger.platform, &ctx->module, &watchpoint); +} + +EXP bool BizClearWatchpoint(bizctx* ctx, ssize_t id) +{ + return ctx->debugger.platform->clearBreakpoint(ctx->debugger.platform, id); +} + +EXP bizctx* BizCreate(const void* bios, const void* data, uint32_t length, const overrideinfo* overrides, bool skipbios) +{ + bizctx* ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + { + return NULL; + } + + ctx->rom = malloc(length); + if (!ctx->rom) + { + free(ctx); + return NULL; + } + + ctx->logger.log = logdebug; + mLogSetDefaultLogger(&ctx->logger); + ctx->skipbios = skipbios; + + memcpy(ctx->rom, data, length); + ctx->romvf = VFileFromMemory(ctx->rom, length); + if (!ctx->romvf) + { + free(ctx->rom); + free(ctx); + return NULL; + } + + ctx->core = GBACoreCreate(); + if (!ctx->core) + { + ctx->romvf->close(ctx->romvf); + free(ctx->rom); + free(ctx); + return NULL; + } + + mCoreInitConfig(ctx->core, NULL); + + if (!ctx->core->init(ctx->core)) + { + BizDestroy(ctx); + return NULL; + } + + ctx->gba = ctx->core->board; + + ctx->core->setVideoBuffer(ctx->core, ctx->vbuff, GBA_VIDEO_HORIZONTAL_PIXELS); + ctx->core->setAudioBufferSize(ctx->core, 1024); + + blip_set_rates(ctx->core->getAudioChannel(ctx->core, 0), ctx->core->frequency(ctx->core), 44100); + blip_set_rates(ctx->core->getAudioChannel(ctx->core, 1), ctx->core->frequency(ctx->core), 44100); + + if (!ctx->core->loadROM(ctx->core, ctx->romvf)) + { + BizDestroy(ctx); + return NULL; + } + + memset(ctx->sram, 0xff, sizeof(ctx->sram)); + ctx->sramvf = VFileFromMemory(ctx->sram, sizeof(ctx->sram)); + if (!ctx->sramvf) + { + BizDestroy(ctx); + return NULL; + } + + ctx->core->loadSave(ctx->core, ctx->sramvf); + + mCoreSetRTC(ctx->core, &ctx->rtcsource); + + ctx->gba->idleOptimization = IDLE_LOOP_IGNORE; // Don't do "idle skipping" + ctx->gba->keyCallback = &ctx->keysource; // Callback for key reading + + ctx->keysource.readKeys = GetKeys; + ctx->rotsource.sample = RotationCB; + ctx->rotsource.readTiltX = GetX; + ctx->rotsource.readTiltY = GetY; + ctx->rotsource.readGyroZ = GetZ; + ctx->lumasource.sample = LightCB; + ctx->lumasource.readLuminance = GetLight; + ctx->rtcsource.sample = TimeCB; + ctx->rtcsource.unixTime = GetTime; + + ctx->core->setPeripheral(ctx->core, mPERIPH_ROTATION, &ctx->rotsource); + ctx->core->setPeripheral(ctx->core, mPERIPH_GBA_LUMINANCE, &ctx->lumasource); + + if (bios) + { + memcpy(ctx->bios, bios, sizeof(ctx->bios)); + ctx->biosvf = VFileFromMemory(ctx->bios, sizeof(ctx->bios)); + /*if (!GBAIsBIOS(ctx->biosvf)) + { + ctx->biosvf->close(ctx->biosvf); + GBADestroy(&ctx->gba); + free(ctx); + return NULL; + }*/ + ctx->core->loadBIOS(ctx->core, ctx->biosvf, 0); + } + + // setup overrides (largely copy paste from GBAOverrideApplyDefaults, with minor changes for our wants) + const struct GBACartridge* cart = (const struct GBACartridge*) ctx->gba->memory.rom; + if (cart) + { + memcpy(ctx->override.id, &cart->id, sizeof(ctx->override.id)); + bool isPokemon = false, isKnownPokemon = false; + + if (overrides->detectPokemonRomHacks) + { + static const uint32_t pokemonTable[] = + { + // Emerald + 0x4881F3F8, // BPEJ + 0x8C4D3108, // BPES + 0x1F1C08FB, // BPEE + 0x34C9DF89, // BPED + 0xA3FDCCB1, // BPEF + 0xA0AEC80A, // BPEI + + // FireRed + 0x1A81EEDF, // BPRD + 0x3B2056E9, // BPRJ + 0x5DC668F6, // BPRF + 0x73A72167, // BPRI + 0x84EE4776, // BPRE rev 1 + 0x9F08064E, // BPRS + 0xBB640DF7, // BPRJ rev 1 + 0xDD88761C, // BPRE + + // Ruby + 0x61641576, // AXVE rev 1 + 0xAEAC73E6, // AXVE rev 2 + 0xF0815EE7, // AXVE + }; + + isPokemon = isPokemon || !strncmp("pokemon red version", &((const char*) ctx->gba->memory.rom)[0x108], 20); + isPokemon = isPokemon || !strncmp("pokemon emerald version", &((const char*) ctx->gba->memory.rom)[0x108], 24); + isPokemon = isPokemon || !strncmp("AXVE", &((const char*) ctx->gba->memory.rom)[0xAC], 4); + + if (isPokemon) + { + size_t i; + for (i = 0; !isKnownPokemon && i < sizeof(pokemonTable) / sizeof(*pokemonTable); ++i) + { + isKnownPokemon = ctx->gba->romCrc32 == pokemonTable[i]; + } + } + } + + if (isPokemon && !isKnownPokemon) + { + ctx->override.savetype = GBA_SAVEDATA_FLASH1M; + ctx->override.hardware = HW_RTC; + ctx->override.vbaBugCompat = true; + } + else + { + GBAOverrideFind(NULL, &ctx->override); // apply defaults + if (overrides->savetype != GBA_SAVEDATA_AUTODETECT) + { + ctx->override.savetype = overrides->savetype; + } + for (int i = 0; i < 5; i++) + { + if (!(overrides->hardware & (128 << i))) + { + if (overrides->hardware & (1 << i)) + { + ctx->override.hardware |= (1 << i); + } + else + { + ctx->override.hardware &= ~(1 << i); + } + } + } + ctx->override.hardware |= overrides->hardware & 64; // gb player detect + ctx->override.vbaBugCompat = overrides->vbaBugCompat; + } + + ctx->override.idleLoop = overrides->idleLoop; + } + + mDebuggerInit(&ctx->debugger); + ctx->module.type = DEBUGGER_CUSTOM; + ctx->module.custom = exec_hook; + ctx->module.entered = watchpoint_entry; + mDebuggerAttachModule(&ctx->debugger, &ctx->module); + mDebuggerAttach(&ctx->debugger, ctx->core); + ctx->attached = true; + + resetinternal(ctx); + + // proper init RTC, our buffer would have trashed it + if (ctx->override.hardware & HW_RTC) + { + GBAHardwareInitRTC(&ctx->gba->memory.hw); + } + + return ctx; +} + +EXP void BizReset(bizctx* ctx) +{ + resetinternal(ctx); +} + +static void blit(uint32_t* dst, const color_t* src, const uint32_t* palette) +{ + uint32_t* dst_end = dst + GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS; + + while (dst < dst_end) + { + *dst++ = palette[*src++]; + } +} + +EXP bool BizAdvance(bizctx* ctx, uint16_t keys, uint32_t* vbuff, uint32_t* nsamp, int16_t* sbuff, + int64_t time, int16_t gyrox, int16_t gyroy, int16_t gyroz, uint8_t luma) +{ + ctx->core->setKeys(ctx->core, keys); + ctx->keys = keys; + ctx->light = luma; + ctx->time = time; + ctx->tiltx = gyrox; + ctx->tilty = gyroy; + ctx->tiltz = gyroz; + ctx->lagged = true; + + ctx->module.needsCallback = ctx->trace_callback || ctx->exec_callback; + ctx->debugger.state = ctx->module.needsCallback ? DEBUGGER_CALLBACK : DEBUGGER_RUNNING; + mDebuggerRunFrame(&ctx->debugger); + + blit(vbuff, ctx->vbuff, ctx->palette); + *nsamp = blip_samples_avail(ctx->core->getAudioChannel(ctx->core, 0)); + if (*nsamp > 1024) + *nsamp = 1024; + blip_read_samples(ctx->core->getAudioChannel(ctx->core, 0), sbuff, 1024, true); + blip_read_samples(ctx->core->getAudioChannel(ctx->core, 1), sbuff + 1, 1024, true); + return ctx->lagged; +} + +EXP void BizSetPalette(bizctx* ctx, const uint32_t* palette) +{ + memcpy(ctx->palette, palette, sizeof(ctx->palette)); +} + +struct MemoryAreas +{ + const void* bios; + const void* wram; + const void* iwram; + const void* mmio; + const void* palram; + const void* vram; + const void* oam; + const void* rom; + const void* sram; +}; + +EXP void BizGetMemoryAreas(bizctx* ctx, struct MemoryAreas* dst) +{ + size_t sizeOut; + dst->bios = ctx->core->getMemoryBlock(ctx->core, GBA_REGION_BIOS, &sizeOut); + dst->wram = ctx->core->getMemoryBlock(ctx->core, GBA_REGION_EWRAM, &sizeOut); + dst->iwram = ctx->core->getMemoryBlock(ctx->core, GBA_REGION_IWRAM, &sizeOut); + dst->mmio = ctx->gba->memory.io; + dst->palram = ctx->core->getMemoryBlock(ctx->core, GBA_REGION_PALETTE_RAM, &sizeOut); + dst->vram = ctx->core->getMemoryBlock(ctx->core, GBA_REGION_VRAM, &sizeOut); + dst->oam = ctx->core->getMemoryBlock(ctx->core, GBA_REGION_OAM, &sizeOut); + dst->rom = ctx->core->getMemoryBlock(ctx->core, GBA_REGION_ROM0, &sizeOut); + // Return the buffer that BizHawk hands to mGBA for storing savedata. + // getMemoryBlock is avoided because mGBA doesn't always know save type at startup, + // so getMemoryBlock may return nothing until the save type is detected. + // (returning the buffer directly avoids 0-size and variable-size savedata) + dst->sram = ctx->sram; + // If ROM is not present (due to multiboot), send our raw buffer over + if (!dst->rom) + { + dst->rom = ctx->rom; + } +} + +EXP uint32_t BizGetSaveRam(bizctx* ctx, void* data, uint32_t size) +{ + GBASavedataRTCWrite(&ctx->gba->memory.savedata); // make sure RTC data is up to date + ctx->sramvf->seek(ctx->sramvf, 0, SEEK_SET); + return ctx->sramvf->read(ctx->sramvf, data, size); +} + +EXP void BizPutSaveRam(bizctx* ctx, const void* data, uint32_t size) +{ + ctx->sramvf->seek(ctx->sramvf, 0, SEEK_SET); + ctx->sramvf->write(ctx->sramvf, data, size); + if ((ctx->override.hardware & HW_RTC) && (size & 0xff)) // RTC data is in the save, read it out + { + GBASavedataRTCRead(&ctx->gba->memory.savedata); + } +} + +// state sizes can vary! +EXP bool BizStartGetState(bizctx* ctx, struct VFile** file, uint32_t* size) +{ + struct VFile* vf = VFileMemChunk(NULL, 0); + if (!mCoreSaveStateNamed(ctx->core, vf, SAVESTATE_SAVEDATA)) + { + vf->close(vf); + return false; + } + *file = vf; + *size = vf->seek(vf, 0, SEEK_END); + return true; +} + +EXP void BizFinishGetState(struct VFile* file, void* data, uint32_t size) +{ + file->seek(file, 0, SEEK_SET); + file->read(file, data, size); + file->close(file); +} + +EXP bool BizPutState(bizctx* ctx, const void* data, uint32_t size) +{ + struct VFile* vf = VFileFromConstMemory(data, size); + bool ret = mCoreLoadStateNamed(ctx->core, vf, SAVESTATE_SAVEDATA); + vf->close(vf); + return ret; +} + +EXP void BizSetLayerMask(bizctx *ctx, uint32_t mask) +{ + for (int i = 0; i < 5; i++) + ctx->core->enableVideoLayer(ctx->core, i, mask & 1 << i); +} + +EXP void BizSetSoundMask(bizctx* ctx, uint32_t mask) +{ + for (int i = 0; i < 6; i++) + ctx->core->enableAudioChannel(ctx->core, i, mask & 1 << i); +} + +EXP void BizGetRegisters(bizctx* ctx, int32_t* dest) +{ + memcpy(dest, ctx->gba->cpu, 18 * sizeof(int32_t)); +} + +EXP void BizSetRegister(bizctx* ctx, int32_t index, int32_t value) +{ + if (index >= 0 && index < 16) + { + ctx->gba->cpu->gprs[index] = value; + } + + if (index == 16) + { + memcpy(&ctx->gba->cpu->cpsr, &value, sizeof(int32_t)); + } + + if (index == 17) + { + memcpy(&ctx->gba->cpu->spsr, &value, sizeof(int32_t)); + } +} + +EXP uint64_t BizGetGlobalTime(bizctx* ctx) +{ + return mTimingGlobalTime(ctx->debugger.core->timing); +} + +EXP void BizWriteBus(bizctx* ctx, uint32_t addr, uint8_t val) +{ + ctx->core->rawWrite8(ctx->core, addr, -1, val); +} + +EXP uint8_t BizReadBus(bizctx* ctx, uint32_t addr) +{ + return ctx->core->busRead8(ctx->core, addr); +} diff --git a/src/platform/bizhawk/linux/Makefile b/src/platform/bizhawk/linux/Makefile new file mode 100644 index 00000000000..ffa357e3341 --- /dev/null +++ b/src/platform/bizhawk/linux/Makefile @@ -0,0 +1,30 @@ +CC ?= gcc +RM ?= rm +CP ?= cp + +MACHINE = $(shell $(CC) -dumpmachine) + +ifneq (,$(findstring i686,$(MACHINE))) + $(error 32 bit build no longer supported) +else ifneq (,$(findstring x86_64,$(MACHINE))) + ARCH = 64 +else + $(error Unknown arch) +endif + +MAKEFILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +ROOT_DIR := $(MAKEFILE_DIR)/../../.. +OBJ_DIR := $(MAKEFILE_DIR)/obj + +LDFLAGS_64 = +LDFLAGS = -shared -s $(LDFLAGS_$(ARCH)) $(CCFLAGS) + +PLAT_SRCS += \ + $(ROOT_DIR)/platform/posix/memory.c + +PLAT_FLAGS += \ + -DHAVE_LOCALE -DHAVE_LOCALTIME_R -fvisibility=hidden -fPIC + +TARGET = libmgba.dll.so + +include ../base.mak diff --git a/src/platform/bizhawk/localtime_r.c b/src/platform/bizhawk/localtime_r.c new file mode 100644 index 00000000000..3dd28f48978 --- /dev/null +++ b/src/platform/bizhawk/localtime_r.c @@ -0,0 +1,179 @@ + +// +// time.c +// +// Time routines +// +// Copyright (C) 2002 Michael Ringgaard. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// +#include + +#define YEAR0 1900 +#define EPOCH_YR 1970 +#define SECS_DAY (24L * 60L * 60L) +#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400))) +#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365) +#define FIRSTSUNDAY(timp) (((timp)->tm_yday - (timp)->tm_wday + 420) % 7) +#define FIRSTDAYOF(timp) (((timp)->tm_wday - (timp)->tm_yday + 420) % 7) + +#define TIME_MAX 2147483647L + +static const int _ytab[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + +#ifdef _WIN32 +errno_t _localtime64_s(struct tm *tmbuf, const time_t *timer) { +#else +struct tm *localtime_r(const time_t *timer, struct tm *tmbuf) { +#endif + time_t time = *timer; + unsigned long dayclock, dayno; + int year = EPOCH_YR; + + dayclock = (unsigned long) time % SECS_DAY; + dayno = (unsigned long) time / SECS_DAY; + + tmbuf->tm_sec = dayclock % 60; + tmbuf->tm_min = (dayclock % 3600) / 60; + tmbuf->tm_hour = dayclock / 3600; + tmbuf->tm_wday = (dayno + 4) % 7; // Day 0 was a thursday + while (dayno >= (unsigned long) YEARSIZE(year)) { + dayno -= YEARSIZE(year); + year++; + } + tmbuf->tm_year = year - YEAR0; + tmbuf->tm_yday = dayno; + tmbuf->tm_mon = 0; + while (dayno >= (unsigned long) _ytab[LEAPYEAR(year)][tmbuf->tm_mon]) { + dayno -= _ytab[LEAPYEAR(year)][tmbuf->tm_mon]; + tmbuf->tm_mon++; + } + tmbuf->tm_mday = dayno + 1; + tmbuf->tm_isdst = 0; +#ifdef _WIN32 + return 0; +#else + return tmbuf; +#endif +} + +#ifdef _WIN32 +time_t _mktime64(struct tm *tmbuf) { +#else +time_t mktime(struct tm *tmbuf) { +#endif + long day, year; + int tm_year; + int yday, month; + /*unsigned*/ long seconds; + int overflow; + + tmbuf->tm_min += tmbuf->tm_sec / 60; + tmbuf->tm_sec %= 60; + if (tmbuf->tm_sec < 0) { + tmbuf->tm_sec += 60; + tmbuf->tm_min--; + } + tmbuf->tm_hour += tmbuf->tm_min / 60; + tmbuf->tm_min = tmbuf->tm_min % 60; + if (tmbuf->tm_min < 0) { + tmbuf->tm_min += 60; + tmbuf->tm_hour--; + } + day = tmbuf->tm_hour / 24; + tmbuf->tm_hour= tmbuf->tm_hour % 24; + if (tmbuf->tm_hour < 0) { + tmbuf->tm_hour += 24; + day--; + } + tmbuf->tm_year += tmbuf->tm_mon / 12; + tmbuf->tm_mon %= 12; + if (tmbuf->tm_mon < 0) { + tmbuf->tm_mon += 12; + tmbuf->tm_year--; + } + day += (tmbuf->tm_mday - 1); + while (day < 0) { + if(--tmbuf->tm_mon < 0) { + tmbuf->tm_year--; + tmbuf->tm_mon = 11; + } + day += _ytab[LEAPYEAR(YEAR0 + tmbuf->tm_year)][tmbuf->tm_mon]; + } + while (day >= _ytab[LEAPYEAR(YEAR0 + tmbuf->tm_year)][tmbuf->tm_mon]) { + day -= _ytab[LEAPYEAR(YEAR0 + tmbuf->tm_year)][tmbuf->tm_mon]; + if (++(tmbuf->tm_mon) == 12) { + tmbuf->tm_mon = 0; + tmbuf->tm_year++; + } + } + tmbuf->tm_mday = day + 1; + year = EPOCH_YR; + if (tmbuf->tm_year < year - YEAR0) return (time_t) -1; + seconds = 0; + day = 0; // Means days since day 0 now + overflow = 0; + + // Assume that when day becomes negative, there will certainly + // be overflow on seconds. + // The check for overflow needs not to be done for leapyears + // divisible by 400. + // The code only works when year (1970) is not a leapyear. + tm_year = tmbuf->tm_year + YEAR0; + + if (TIME_MAX / 365 < tm_year - year) overflow++; + day = (tm_year - year) * 365; + if (TIME_MAX - day < (tm_year - year) / 4 + 1) overflow++; + day += (tm_year - year) / 4 + ((tm_year % 4) && tm_year % 4 < year % 4); + day -= (tm_year - year) / 100 + ((tm_year % 100) && tm_year % 100 < year % 100); + day += (tm_year - year) / 400 + ((tm_year % 400) && tm_year % 400 < year % 400); + + yday = month = 0; + while (month < tmbuf->tm_mon) { + yday += _ytab[LEAPYEAR(tm_year)][month]; + month++; + } + yday += (tmbuf->tm_mday - 1); + if (day + yday < 0) overflow++; + day += yday; + + tmbuf->tm_yday = yday; + tmbuf->tm_wday = (day + 4) % 7; // Day 0 was thursday (4) + + seconds = ((tmbuf->tm_hour * 60L) + tmbuf->tm_min) * 60L + tmbuf->tm_sec; + + if ((TIME_MAX - seconds) / SECS_DAY < day) overflow++; + seconds += day * SECS_DAY; + + if (overflow) return (time_t) -1; + + if ((time_t) seconds != seconds) return (time_t) -1; + return (time_t) seconds; +} diff --git a/src/platform/bizhawk/mingw/Makefile b/src/platform/bizhawk/mingw/Makefile new file mode 100644 index 00000000000..2671449758b --- /dev/null +++ b/src/platform/bizhawk/mingw/Makefile @@ -0,0 +1,31 @@ +CC ?= gcc +RM ?= rm +CP ?= cp + +MACHINE = $(shell $(CC) -dumpmachine) + +ifneq (,$(findstring i686,$(MACHINE))) + $(error 32 bit build no longer supported) +else ifneq (,$(findstring x86_64,$(MACHINE))) + ARCH = 64 +else + $(error Unknown arch) +endif + +MAKEFILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +ROOT_DIR := $(MAKEFILE_DIR)/../../.. +OBJ_DIR := $(MAKEFILE_DIR)/obj + +LDFLAGS_32 = -static-libgcc +LDFLAGS_64 = +LDFLAGS = -shared -s $(LDFLAGS_$(ARCH)) $(CCFLAGS) -lshlwapi + +PLAT_SRCS += \ + $(ROOT_DIR)/platform/windows/memory.c + +PLAT_FLAGS += \ + -Wno-format -Wno-inconsistent-dllimport -D_mktime64=biz_mktime64 -D_localtime64_s=biz_localtime64_s + +TARGET = mgba.dll + +include ../base.mak