all repos — mgba @ 3d27d1d7fe6fbce0334a7411eb4f709e2b17c040

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