all repos — mgba @ e7be40e80ccb6dbeee0b79c97f349859b1b4afb8

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