all repos — mgba @ a1cdd65e19e73e4ddfbd828582f83d6fb0c48fe0

mGBA Game Boy Advance Emulator

src/gba/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/gba/renderers/proxy.h>
  7
  8#include <mgba/core/cache-set.h>
  9#include <mgba/internal/gba/gba.h>
 10#include <mgba/internal/gba/io.h>
 11
 12static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer);
 13static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer);
 14static void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer);
 15static uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 16static void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
 17static void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 18static void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 19static void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 20static void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
 21static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
 22static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
 23
 24static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event);
 25static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
 26static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
 27
 28void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
 29	renderer->d.init = GBAVideoProxyRendererInit;
 30	renderer->d.reset = GBAVideoProxyRendererReset;
 31	renderer->d.deinit = GBAVideoProxyRendererDeinit;
 32	renderer->d.writeVideoRegister = GBAVideoProxyRendererWriteVideoRegister;
 33	renderer->d.writeVRAM = GBAVideoProxyRendererWriteVRAM;
 34	renderer->d.writeOAM = GBAVideoProxyRendererWriteOAM;
 35	renderer->d.writePalette = GBAVideoProxyRendererWritePalette;
 36	renderer->d.drawScanline = GBAVideoProxyRendererDrawScanline;
 37	renderer->d.finishFrame = GBAVideoProxyRendererFinishFrame;
 38	renderer->d.getPixels = GBAVideoProxyRendererGetPixels;
 39	renderer->d.putPixels = GBAVideoProxyRendererPutPixels;
 40
 41	renderer->d.disableBG[0] = false;
 42	renderer->d.disableBG[1] = false;
 43	renderer->d.disableBG[2] = false;
 44	renderer->d.disableBG[3] = false;
 45	renderer->d.disableOBJ = false;
 46
 47	renderer->d.highlightBG[0] = false;
 48	renderer->d.highlightBG[1] = false;
 49	renderer->d.highlightBG[2] = false;
 50	renderer->d.highlightBG[3] = false;
 51	int i;
 52	for (i = 0; i < 128; ++i) {
 53		renderer->d.highlightOBJ[i] = false;
 54	}
 55	renderer->d.highlightColor = 0xFFFFFF;
 56	renderer->d.highlightAmount = 0;
 57
 58	renderer->logger->context = renderer;
 59	renderer->logger->parsePacket = _parsePacket;
 60	renderer->logger->handleEvent = _handleEvent;
 61	renderer->logger->vramBlock = _vramBlock;
 62	renderer->logger->paletteSize = SIZE_PALETTE_RAM;
 63	renderer->logger->vramSize = SIZE_VRAM;
 64	renderer->logger->oamSize = SIZE_OAM;
 65
 66	renderer->backend = backend;
 67}
 68
 69static void _init(struct GBAVideoProxyRenderer* proxyRenderer) {
 70	mVideoLoggerRendererInit(proxyRenderer->logger);
 71
 72	if (proxyRenderer->logger->block) {
 73		proxyRenderer->backend->palette = proxyRenderer->logger->palette;
 74		proxyRenderer->backend->vram = proxyRenderer->logger->vram;
 75		proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger->oam;
 76		proxyRenderer->backend->cache = NULL;
 77	}
 78}
 79
 80static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
 81	memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM);
 82	memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM);
 83	memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM);
 84
 85	mVideoLoggerRendererReset(proxyRenderer->logger);
 86}
 87
 88void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
 89	if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
 90		return;
 91	}
 92	renderer->backend = video->renderer;
 93	video->renderer = &renderer->d;
 94	renderer->d.cache = renderer->backend->cache;
 95	renderer->d.palette = video->palette;
 96	renderer->d.vram = video->vram;
 97	renderer->d.oam = &video->oam;
 98	_init(renderer);
 99	_reset(renderer);
100}
101
102void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
103	if (video->renderer != &renderer->d) {
104		return;
105	}
106	renderer->backend->cache = video->renderer->cache;
107	video->renderer = renderer->backend;
108	renderer->backend->palette = video->palette;
109	renderer->backend->vram = video->vram;
110	renderer->backend->oam = &video->oam;
111
112	mVideoLoggerRendererDeinit(renderer->logger);
113}
114
115void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) {
116	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
117
118	_init(proxyRenderer);
119	_reset(proxyRenderer);
120
121	if (!proxyRenderer->logger->block) {
122		proxyRenderer->backend->init(proxyRenderer->backend);
123	} else {
124		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_INIT);
125	}
126}
127
128void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) {
129	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
130
131	_reset(proxyRenderer);
132
133	if (!proxyRenderer->logger->block) {
134		proxyRenderer->backend->reset(proxyRenderer->backend);
135	} else {
136		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_RESET);
137	}
138}
139
140void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
141	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
142
143	if (!proxyRenderer->logger->block) {
144		proxyRenderer->backend->deinit(proxyRenderer->backend);
145	} else {
146		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
147		mVideoLoggerRendererFlush(proxyRenderer->logger);
148	}
149
150	mVideoLoggerRendererDeinit(proxyRenderer->logger);
151}
152
153static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event) {
154	struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
155	switch (event) {
156	default:
157		break;
158	case LOGGER_EVENT_INIT:
159		proxyRenderer->backend->init(proxyRenderer->backend);
160		break;
161	case LOGGER_EVENT_DEINIT:
162		proxyRenderer->backend->deinit(proxyRenderer->backend);
163		break;
164	case LOGGER_EVENT_RESET:
165		proxyRenderer->backend->reset(proxyRenderer->backend);
166		break;
167	case LOGGER_EVENT_GET_PIXELS:
168		proxyRenderer->backend->getPixels(proxyRenderer->backend, &logger->pixelStride, &logger->pixelBuffer);
169		break;
170	}
171}
172
173static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
174	struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
175	switch (item->type) {
176	case DIRTY_REGISTER:
177		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
178		break;
179	case DIRTY_PALETTE:
180		if (item->address < SIZE_PALETTE_RAM) {
181			logger->palette[item->address >> 1] = item->value;
182			proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
183		}
184		break;
185	case DIRTY_OAM:
186		if (item->address < SIZE_OAM) {
187			logger->oam[item->address] = item->value;
188			proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
189		}
190		break;
191	case DIRTY_VRAM:
192		if (item->address <= SIZE_VRAM - 0x1000) {
193			logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
194			proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
195		} else {
196			logger->readData(logger, NULL, 0x1000, true);
197		}
198		break;
199	case DIRTY_SCANLINE:
200		proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
201		proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
202		proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
203		proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
204		proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
205		proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0];
206		proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1];
207		proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2];
208		proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3];
209		memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ));
210		proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount;
211		if (item->address < GBA_VIDEO_VERTICAL_PIXELS) {
212			proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
213		}
214		break;
215	case DIRTY_FRAME:
216		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
217		break;
218	case DIRTY_FLUSH:
219		return false;
220	default:
221		return false;
222	}
223	return true;
224}
225
226static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
227	struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
228	return &proxyRenderer->d.vram[address >> 1];
229}
230
231uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
232	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
233	switch (address) {
234	case REG_DISPCNT:
235		value &= 0xFFF7;
236		break;
237	case REG_BG0CNT:
238	case REG_BG1CNT:
239		value &= 0xDFFF;
240		break;
241	case REG_BG2CNT:
242	case REG_BG3CNT:
243		value &= 0xFFFF;
244		break;
245	case REG_BG0HOFS:
246	case REG_BG0VOFS:
247	case REG_BG1HOFS:
248	case REG_BG1VOFS:
249	case REG_BG2HOFS:
250	case REG_BG2VOFS:
251	case REG_BG3HOFS:
252	case REG_BG3VOFS:
253		value &= 0x01FF;
254		break;
255	}
256	if (address > REG_BLDY) {
257		return value;
258	}
259
260	mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
261	if (!proxyRenderer->logger->block) {
262		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
263	}
264	return value;
265}
266
267void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
268	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
269	mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
270	if (!proxyRenderer->logger->block) {
271		proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
272	}
273	if (renderer->cache) {
274		mCacheSetWriteVRAM(renderer->cache, address);
275	}
276}
277
278void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
279	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
280	mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
281	if (!proxyRenderer->logger->block) {
282		proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
283	}
284	if (renderer->cache) {
285		mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
286	}
287}
288
289void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
290	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
291	if (!proxyRenderer->logger->block) {
292		proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
293	}
294	mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]);
295}
296
297void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
298	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
299	if (!proxyRenderer->logger->block) {
300		proxyRenderer->backend->drawScanline(proxyRenderer->backend, y);
301	}
302	mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
303	if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
304		proxyRenderer->logger->wake(proxyRenderer->logger, y);
305	}
306}
307
308void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
309	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
310	if (!proxyRenderer->logger->block) {
311		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
312	}
313	mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
314	mVideoLoggerRendererFlush(proxyRenderer->logger);
315}
316
317static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
318	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
319	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
320		// Insert an extra item into the queue to make sure it gets flushed
321		mVideoLoggerRendererFlush(proxyRenderer->logger);
322		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
323		mVideoLoggerRendererFlush(proxyRenderer->logger);
324		*pixels = proxyRenderer->logger->pixelBuffer;
325		*stride = proxyRenderer->logger->pixelStride;
326	} else {
327		proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
328	}
329}
330
331static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
332	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
333	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
334		proxyRenderer->logger->lock(proxyRenderer->logger);
335	}
336	proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
337	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
338		proxyRenderer->logger->unlock(proxyRenderer->logger);
339	}
340}