all repos — mgba @ 6174858d0d6a8de2e153dd39ae323bfce8c0c921

mGBA Game Boy Advance Emulator

src/gb/extra/proxy.c (view raw)

  1/* Copyright (c) 2013-2017 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/internal/gb/renderers/proxy.h>
  7
  8#include <mgba/core/cache-set.h>
  9#include <mgba/internal/gb/gb.h>
 10#include <mgba/internal/gb/io.h>
 11
 12#define BUFFER_OAM 1
 13#define BUFFER_SGB 2
 14
 15static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
 16static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer);
 17static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
 18static void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
 19static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
 20static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
 21static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value);
 22static void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y);
 23static void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
 24static void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer);
 25static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable);
 26static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
 27static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
 28
 29static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
 30static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
 31
 32void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend) {
 33	renderer->d.init = GBVideoProxyRendererInit;
 34	renderer->d.deinit = GBVideoProxyRendererDeinit;
 35	renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister;
 36	renderer->d.writeSGBPacket = GBVideoProxyRendererWriteSGBPacket;
 37	renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM;
 38	renderer->d.writeOAM = GBVideoProxyRendererWriteOAM;
 39	renderer->d.writePalette = GBVideoProxyRendererWritePalette;
 40	renderer->d.drawRange = GBVideoProxyRendererDrawRange;
 41	renderer->d.finishScanline = GBVideoProxyRendererFinishScanline;
 42	renderer->d.finishFrame = GBVideoProxyRendererFinishFrame;
 43	renderer->d.enableSGBBorder = GBVideoProxyRendererEnableSGBBorder;
 44	renderer->d.getPixels = GBVideoProxyRendererGetPixels;
 45	renderer->d.putPixels = GBVideoProxyRendererPutPixels;
 46
 47	renderer->d.disableBG = false;
 48	renderer->d.disableWIN = false;
 49	renderer->d.disableOBJ = false;
 50
 51	renderer->logger->context = renderer;
 52	renderer->logger->parsePacket = _parsePacket;
 53	renderer->logger->vramBlock = _vramBlock;
 54	renderer->logger->paletteSize = 0;
 55	renderer->logger->vramSize = GB_SIZE_VRAM;
 56	renderer->logger->oamSize = GB_SIZE_OAM;
 57
 58	renderer->backend = backend;
 59}
 60
 61static void _init(struct GBVideoProxyRenderer* proxyRenderer) {
 62	mVideoLoggerRendererInit(proxyRenderer->logger);
 63
 64	if (proxyRenderer->logger->block) {
 65		proxyRenderer->backend->vram = (uint8_t*) proxyRenderer->logger->vram;
 66		proxyRenderer->backend->oam = (union GBOAM*) proxyRenderer->logger->oam;
 67		proxyRenderer->backend->cache = NULL;
 68	}
 69}
 70
 71static void _reset(struct GBVideoProxyRenderer* proxyRenderer) {
 72	memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM);
 73	memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM);
 74
 75	mVideoLoggerRendererReset(proxyRenderer->logger);
 76}
 77
 78void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
 79	if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
 80		return;
 81	}
 82	renderer->backend = video->renderer;
 83	video->renderer = &renderer->d;
 84	renderer->d.cache = renderer->backend->cache;
 85	renderer->d.vram = video->vram;
 86	renderer->d.oam = &video->oam;
 87	_init(renderer);
 88	_reset(renderer);
 89}
 90
 91void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
 92	if (video->renderer != &renderer->d) {
 93		return;
 94	}
 95	renderer->backend->cache = video->renderer->cache;
 96	video->renderer = renderer->backend;
 97	renderer->backend->vram = video->vram;
 98	renderer->backend->oam = &video->oam;
 99
100	mVideoLoggerRendererDeinit(renderer->logger);
101}
102
103void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders) {
104	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
105
106	_init(proxyRenderer);
107
108	proxyRenderer->backend->init(proxyRenderer->backend, model, borders);
109}
110
111void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
112	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
113
114	proxyRenderer->backend->deinit(proxyRenderer->backend);
115
116	mVideoLoggerRendererDeinit(proxyRenderer->logger);
117}
118
119static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
120	struct GBVideoProxyRenderer* proxyRenderer = logger->context;
121	uint8_t sgbPacket[16];
122	struct GBObj legacyBuffer[GB_VIDEO_MAX_OBJ];
123	switch (item->type) {
124	case DIRTY_REGISTER:
125		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
126		break;
127	case DIRTY_PALETTE:
128		if (item->address < 64) {
129			proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
130		}
131		break;
132	case DIRTY_OAM:
133		if (item->address < GB_SIZE_OAM) {
134			((uint8_t*) logger->oam)[item->address] = item->value;
135			proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
136		}
137		break;
138	case DIRTY_VRAM:
139		if (item->address <= GB_SIZE_VRAM - 0x1000) {
140			logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
141			proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
142		}
143		break;
144	case DIRTY_SCANLINE:
145		proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
146		proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
147		proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
148		if (item->address < GB_VIDEO_VERTICAL_PIXELS) {
149			proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address);
150		}
151		break;
152	case DIRTY_RANGE:
153		if (item->value < item->value2 && item->value2 <= GB_VIDEO_HORIZONTAL_PIXELS && item->address < GB_VIDEO_VERTICAL_PIXELS) {
154			proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address);
155		}
156		break;
157	case DIRTY_FRAME:
158		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
159		break;
160	case DIRTY_BUFFER:
161		switch (item->address) {
162		case BUFFER_OAM:
163			if (item->value2 / sizeof(struct GBObj) > GB_VIDEO_MAX_OBJ) {
164				return false;
165			}
166			logger->readData(logger, legacyBuffer, item->value2, true);
167			break;
168		case BUFFER_SGB:
169			logger->readData(logger, sgbPacket, 16, true);
170			proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, sgbPacket);
171			break;
172		}
173		break;
174	case DIRTY_FLUSH:
175		return false;
176	default:
177		return false;
178	}
179	return true;
180}
181
182static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
183	struct GBVideoProxyRenderer* proxyRenderer = logger->context;
184	return (uint16_t*) &proxyRenderer->d.vram[address];
185}
186
187uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
188	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
189
190	mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
191	if (!proxyRenderer->logger->block) {
192		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
193	}
194	return value;
195}
196
197void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
198	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
199	if (!proxyRenderer->logger->block) {
200		proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, data);
201	}
202	mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_SGB, 0, 16, data);
203}
204
205void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
206	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
207	mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
208	if (!proxyRenderer->logger->block) {
209		proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
210	}
211	if (renderer->cache) {
212		mCacheSetWriteVRAM(renderer->cache, address);
213	}
214}
215
216void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value) {
217	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
218	mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
219	if (!proxyRenderer->logger->block) {
220		proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
221	}
222	if (renderer->cache) {
223		mCacheSetWritePalette(renderer->cache, address, mColorFrom555(value));
224	}
225}
226
227void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
228	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
229	if (!proxyRenderer->logger->block) {
230		proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
231	}
232	mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, ((uint8_t*) proxyRenderer->d.oam->raw)[oam]);
233}
234
235void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y) {
236	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
237	if (!proxyRenderer->logger->block) {
238		proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
239		proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
240		proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
241		proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y);
242	}
243	mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y);	
244}
245
246void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
247	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
248	if (!proxyRenderer->logger->block) {
249		proxyRenderer->backend->finishScanline(proxyRenderer->backend, y);
250	}
251	mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
252	if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
253		proxyRenderer->logger->wake(proxyRenderer->logger, y);
254	}
255}
256
257void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
258	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
259	if (!proxyRenderer->logger->block) {
260		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
261	}
262	mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
263	mVideoLoggerRendererFlush(proxyRenderer->logger);
264}
265
266static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable) {
267	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
268	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
269		// Insert an extra item into the queue to make sure it gets flushed
270		mVideoLoggerRendererFlush(proxyRenderer->logger);
271		proxyRenderer->logger->wait(proxyRenderer->logger);
272	}
273	proxyRenderer->backend->enableSGBBorder(proxyRenderer->backend, enable);
274}
275
276static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
277	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
278	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
279		proxyRenderer->logger->lock(proxyRenderer->logger);
280		// Insert an extra item into the queue to make sure it gets flushed
281		mVideoLoggerRendererFlush(proxyRenderer->logger);
282		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
283		mVideoLoggerRendererFlush(proxyRenderer->logger);
284		proxyRenderer->logger->unlock(proxyRenderer->logger);
285		*pixels = proxyRenderer->logger->pixelBuffer;
286		*stride = proxyRenderer->logger->pixelStride;
287	} else {
288		proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
289	}
290}
291
292static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
293	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
294	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
295		proxyRenderer->logger->lock(proxyRenderer->logger);
296	}
297	proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
298	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
299		proxyRenderer->logger->unlock(proxyRenderer->logger);
300	}
301}