GB: Video tester
@@ -12,6 +12,7 @@ CXX_GUARD_START
struct mCore; struct mCore* GBCoreCreate(void); +struct mCore* GBVideoLogPlayerCreate(void); CXX_GUARD_END
@@ -0,0 +1,31 @@
+/* Copyright (c) 2013-2017 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/. */ +#ifndef GB_VIDEO_PROXY_H +#define GB_VIDEO_PROXY_H + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#include <mgba/internal/gb/video.h> +#include "feature/video-logger.h" + +struct GBVideoProxyRenderer { + struct GBVideoRenderer d; + struct GBVideoRenderer* backend; + struct mVideoLogger* logger; + + struct GBObj objThisLine[40]; + size_t oamMax; +}; + +void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend); +void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer); +void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer); + +CXX_GUARD_END + +#endif
@@ -61,6 +61,7 @@
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address); void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value); + void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam); void (*drawRange)(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* objOnLine, size_t nObj); void (*finishScanline)(struct GBVideoRenderer* renderer, int y); void (*finishFrame)(struct GBVideoRenderer* renderer);
@@ -12,6 +12,9 @@
#ifdef M_CORE_GBA #include <mgba/gba/core.h> #endif +#ifdef M_CORE_GB +#include <mgba/gb/core.h> +#endif const char mVL_MAGIC[] = "mVL\0";@@ -21,6 +24,9 @@ struct mCore* (*open)(void);
} _descriptors[] = { #ifdef M_CORE_GBA { PLATFORM_GBA, GBAVideoLogPlayerCreate }, +#endif +#ifdef M_CORE_GB + { PLATFORM_GB, GBVideoLogPlayerCreate }, #endif { PLATFORM_NONE, 0 } };
@@ -12,6 +12,7 @@ #include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/mbc.h> #include <mgba/internal/gb/overrides.h> #include <mgba/internal/gb/renderers/software.h> +#include <mgba/internal/gb/renderers/proxy.h> #include <mgba/internal/gb/serialize.h> #include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/lr35902/debugger/debugger.h>@@ -36,6 +37,9 @@
struct GBCore { struct mCore d; struct GBVideoSoftwareRenderer renderer; + struct GBVideoProxyRenderer proxyRenderer; + struct mVideoLogContext* logContext; + struct mCoreCallbacks logCallbacks; uint8_t keys; struct mCPUComponent* components[CPU_COMPONENT_MAX]; const struct Configuration* overrides;@@ -635,6 +639,42 @@ break;
} } +static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) { + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = core->board; + gbcore->logContext = context; + + context->initialStateSize = core->stateSize(core); + context->initialState = anonymousMemoryMap(context->initialStateSize); + core->saveState(core, context->initialState); + + struct VFile* vf = VFileMemChunk(NULL, 0); + context->nChannels = 1; + context->channels[0].initialState = NULL; + context->channels[0].initialStateSize = 0; + context->channels[0].channelData = vf; + context->channels[0].type = 0; + gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); + mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false); + + gbcore->proxyRenderer.logger->vf = vf; + gbcore->proxyRenderer.logger->block = false; + + GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d); + GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); +} + +static void _GBCoreEndVideoLog(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = core->board; + GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); + free(gbcore->proxyRenderer.logger); + gbcore->proxyRenderer.logger = NULL; + + mappedMemoryFree(gbcore->logContext->initialState, gbcore->logContext->initialStateSize); + gbcore->logContext->channels[0].channelData->close(gbcore->logContext->channels[0].channelData); +} + struct mCore* GBCoreCreate(void) { struct GBCore* gbcore = malloc(sizeof(*gbcore)); struct mCore* core = &gbcore->d;@@ -707,5 +747,102 @@ core->listVideoLayers = _GBCoreListVideoLayers;
core->listAudioChannels = _GBCoreListAudioChannels; core->enableVideoLayer = _GBCoreEnableVideoLayer; core->enableAudioChannel = _GBCoreEnableAudioChannel; + core->startVideoLog = _GBCoreStartVideoLog; + core->endVideoLog = _GBCoreEndVideoLog; return core; } + +#ifndef MINIMAL_CORE +static void _GBVLPStartFrameCallback(void *context) { + struct mCore* core = context; + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = core->board; + + if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) { + GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); + gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET); + core->loadState(core, gbcore->logContext->initialState); + GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); + + // Make sure CPU loop never spins + GBHalt(gb->cpu); + gb->memory.ie = 0; + gb->memory.ime = false; + } +} + +static bool _GBVLPInit(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + if (!_GBCoreInit(core)) { + return false; + } + gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); + mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, true); + GBVideoProxyRendererCreate(&gbcore->proxyRenderer, NULL); + memset(&gbcore->logCallbacks, 0, sizeof(gbcore->logCallbacks)); + gbcore->logCallbacks.videoFrameStarted = _GBVLPStartFrameCallback; + gbcore->logCallbacks.context = core; + core->addCoreCallbacks(core, &gbcore->logCallbacks); + return true; +} + +static void _GBVLPDeinit(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + if (gbcore->logContext) { + mVideoLoggerDestroy(core, gbcore->logContext); + } + _GBCoreDeinit(core); +} + +static void _GBVLPReset(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = (struct GB*) core->board; + if (gb->video.renderer == &gbcore->proxyRenderer.d) { + GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); + } else if (gbcore->renderer.outputBuffer) { + struct GBVideoRenderer* renderer = &gbcore->renderer.d; + GBVideoAssociateRenderer(&gb->video, renderer); + } + gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET); + + LR35902Reset(core->cpu); + core->loadState(core, gbcore->logContext->initialState); + GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); + + // Make sure CPU loop never spins + GBHalt(gb->cpu); + gb->memory.ie = 0; + gb->memory.ime = false; +} + +static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) { + struct GBCore* gbcore = (struct GBCore*) core; + gbcore->logContext = mVideoLoggerCreate(NULL); + if (!mVideoLogContextLoad(vf, gbcore->logContext)) { + mVideoLoggerDestroy(core, gbcore->logContext); + gbcore->logContext = NULL; + return false; + } + gbcore->proxyRenderer.logger->vf = gbcore->logContext->channels[0].channelData; + return true; +} + +static bool _returnTrue(struct VFile* vf) { + UNUSED(vf); + return true; +} + +struct mCore* GBVideoLogPlayerCreate(void) { + struct mCore* core = GBCoreCreate(); + core->init = _GBVLPInit; + core->deinit = _GBVLPDeinit; + core->reset = _GBVLPReset; + core->loadROM = _GBVLPLoadROM; + core->isROM = _returnTrue; + return core; +} +#else +struct mCore* GBVideoLogPlayerCreate(void) { + return false; +} +#endif
@@ -258,6 +258,7 @@ memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
} else if (address < GB_BASE_UNUSABLE) { if (gb->video.mode < 2) { gb->video.oam.raw[address & 0xFF] = value; + gb->video.renderer->writeOAM(gb->video.renderer, address & 0xFF); } } else if (address < GB_BASE_IO) { mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value);@@ -395,6 +396,7 @@ struct GB* gb = context;
uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource); // TODO: Can DMA write OAM during modes 2-3? gb->video.oam.raw[gb->memory.dmaDest] = b; + gb->video.renderer->writeOAM(gb->video.renderer, gb->memory.dmaDest); ++gb->memory.dmaSource; ++gb->memory.dmaDest; --gb->memory.dmaRemaining;@@ -559,6 +561,7 @@ memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
} else if (address < GB_BASE_UNUSABLE) { oldValue = gb->video.oam.raw[address & 0xFF]; gb->video.oam.raw[address & 0xFF] = value; + gb->video.renderer->writeOAM(gb->video.renderer, address & 0xFF); } else if (address < GB_BASE_HRAM) { mLOG(GB_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); return;
@@ -0,0 +1,258 @@
+/* Copyright (c) 2013-2017 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 <mgba/internal/gb/renderers/proxy.h> + +#include <mgba/core/tile-cache.h> +#include <mgba/internal/gb/gb.h> +#include <mgba/internal/gb/io.h> + +#define BUFFER_OAM 1 + +static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); +static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer); +static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); +static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); +static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); +static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value); +static void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax); +static void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y); +static void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer); +static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels); +static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels); + +static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); +static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); + +void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend) { + renderer->d.init = GBVideoProxyRendererInit; + renderer->d.deinit = GBVideoProxyRendererDeinit; + renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister; + renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM; + renderer->d.writeOAM = GBVideoProxyRendererWriteOAM; + renderer->d.writePalette = GBVideoProxyRendererWritePalette; + renderer->d.drawRange = GBVideoProxyRendererDrawRange; + renderer->d.finishScanline = GBVideoProxyRendererFinishScanline; + renderer->d.finishFrame = GBVideoProxyRendererFinishFrame; + renderer->d.getPixels = GBVideoProxyRendererGetPixels; + renderer->d.putPixels = GBVideoProxyRendererPutPixels; + + renderer->logger->context = renderer; + renderer->logger->parsePacket = _parsePacket; + renderer->logger->vramBlock = _vramBlock; + renderer->logger->paletteSize = 0; + renderer->logger->vramSize = GB_SIZE_VRAM; + renderer->logger->oamSize = GB_SIZE_OAM; + + renderer->backend = backend; +} + +static void _init(struct GBVideoProxyRenderer* proxyRenderer) { + mVideoLoggerRendererInit(proxyRenderer->logger); + + if (proxyRenderer->logger->block) { + proxyRenderer->backend->vram = (uint8_t*) proxyRenderer->logger->vram; + proxyRenderer->backend->oam = (union GBOAM*) proxyRenderer->logger->oam; + proxyRenderer->backend->cache = NULL; + } +} + +static void _reset(struct GBVideoProxyRenderer* proxyRenderer) { + memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM); + memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM); + + mVideoLoggerRendererReset(proxyRenderer->logger); +} + +void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) { + if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) { + return; + } + renderer->backend = video->renderer; + video->renderer = &renderer->d; + renderer->d.cache = renderer->backend->cache; + renderer->d.vram = video->vram; + renderer->d.oam = &video->oam; + _init(renderer); + _reset(renderer); +} + +void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) { + if (video->renderer != &renderer->d) { + return; + } + renderer->backend->cache = video->renderer->cache; + video->renderer = renderer->backend; + renderer->backend->vram = video->vram; + renderer->backend->oam = &video->oam; + + mVideoLoggerRendererDeinit(renderer->logger); +} + +void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + + _init(proxyRenderer); + + proxyRenderer->backend->init(proxyRenderer->backend, model); +} + +void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + + proxyRenderer->backend->deinit(proxyRenderer->backend); + + mVideoLoggerRendererDeinit(proxyRenderer->logger); +} + +static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) { + struct GBVideoProxyRenderer* proxyRenderer = logger->context; + switch (item->type) { + case DIRTY_REGISTER: + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_PALETTE: + proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_OAM: + logger->oam[item->address] = item->value; + proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + break; + case DIRTY_VRAM: + logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + break; + case DIRTY_SCANLINE: + proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address); + break; + case DIRTY_RANGE: + proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address, proxyRenderer->objThisLine, proxyRenderer->oamMax); + break; + case DIRTY_FRAME: + proxyRenderer->backend->finishFrame(proxyRenderer->backend); + break; + case DIRTY_BUFFER: + switch (item->address) { + case BUFFER_OAM: + proxyRenderer->oamMax = item->value2 / sizeof(struct GBObj); + if (proxyRenderer->oamMax > 40) { + return false; + } + logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true); + } + break; + case DIRTY_FLUSH: + return false; + default: + return false; + } + return true; +} + +static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) { + struct GBVideoProxyRenderer* proxyRenderer = logger->context; + return (uint16_t*) &proxyRenderer->d.vram[address]; +} + +uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + + mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value); + } + return value; +} + +void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address); + } + if (renderer->cache) { + mTileCacheWriteVRAM(renderer->cache, address); + } +} + +void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value); + } + if (renderer->cache) { + mTileCacheWritePalette(renderer->cache, address); + } +} + +void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam); + } + mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, ((uint8_t*) proxyRenderer->d.oam->raw)[oam]); +} + +void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y, obj, oamMax); + } + mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_OAM, 0, oamMax * sizeof(*obj), obj); + mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y); +} + +void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->finishScanline(proxyRenderer->backend, y); + } + mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y); + if (proxyRenderer->logger->block && proxyRenderer->logger->wake) { + proxyRenderer->logger->wake(proxyRenderer->logger, y); + } +} + +void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->lock(proxyRenderer->logger); + proxyRenderer->logger->wait(proxyRenderer->logger); + } + mVideoLoggerRendererFinishFrame(proxyRenderer->logger); + mVideoLoggerRendererFlush(proxyRenderer->logger); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->unlock(proxyRenderer->logger); + } +} + +static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->lock(proxyRenderer->logger); + // Insert an extra item into the queue to make sure it gets flushed + mVideoLoggerRendererFlush(proxyRenderer->logger); + proxyRenderer->logger->wait(proxyRenderer->logger); + } + proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->unlock(proxyRenderer->logger); + } +} + +static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->lock(proxyRenderer->logger); + // Insert an extra item into the queue to make sure it gets flushed + mVideoLoggerRendererFlush(proxyRenderer->logger); + proxyRenderer->logger->wait(proxyRenderer->logger); + } + proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->unlock(proxyRenderer->logger); + } +}
@@ -14,6 +14,7 @@ static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); +static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax); static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y); static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);@@ -43,6 +44,7 @@ renderer->d.deinit = GBVideoSoftwareRendererDeinit;
renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister; renderer->d.writePalette = GBVideoSoftwareRendererWritePalette; renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM; + renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM; renderer->d.drawRange = GBVideoSoftwareRendererDrawRange; renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline; renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;@@ -124,6 +126,12 @@ static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
if (renderer->cache) { mTileCacheWriteVRAM(renderer->cache, address); } +} + +static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) { + UNUSED(renderer); + UNUSED(oam); + // Nothing to do } static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
@@ -20,6 +20,7 @@ static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); +static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax); static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y); static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);@@ -39,6 +40,7 @@ .init = GBVideoDummyRendererInit,
.deinit = GBVideoDummyRendererDeinit, .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, .writeVRAM = GBVideoDummyRendererWriteVRAM, + .writeOAM = GBVideoDummyRendererWriteOAM, .writePalette = GBVideoDummyRendererWritePalette, .drawRange = GBVideoDummyRendererDrawRange, .finishScanline = GBVideoDummyRendererFinishScanline,@@ -467,6 +469,12 @@ static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
if (renderer->cache) { mTileCacheWriteVRAM(renderer->cache, address); } +} + +static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) { + UNUSED(renderer); + UNUSED(oam); + // Nothing to do } static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {