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 renderer->d.disableWIN[0] = false;
47 renderer->d.disableWIN[1] = false;
48 renderer->d.disableOBJWIN = false;
49
50 renderer->d.highlightBG[0] = false;
51 renderer->d.highlightBG[1] = false;
52 renderer->d.highlightBG[2] = false;
53 renderer->d.highlightBG[3] = false;
54 int i;
55 for (i = 0; i < 128; ++i) {
56 renderer->d.highlightOBJ[i] = false;
57 }
58 renderer->d.highlightColor = 0xFFFFFF;
59 renderer->d.highlightAmount = 0;
60
61 renderer->logger->context = renderer;
62 renderer->logger->parsePacket = _parsePacket;
63 renderer->logger->handleEvent = _handleEvent;
64 renderer->logger->vramBlock = _vramBlock;
65 renderer->logger->paletteSize = SIZE_PALETTE_RAM;
66 renderer->logger->vramSize = SIZE_VRAM;
67 renderer->logger->oamSize = SIZE_OAM;
68
69 renderer->backend = backend;
70}
71
72static void _init(struct GBAVideoProxyRenderer* proxyRenderer) {
73 mVideoLoggerRendererInit(proxyRenderer->logger);
74
75 if (proxyRenderer->logger->block) {
76 proxyRenderer->backend->palette = proxyRenderer->logger->palette;
77 proxyRenderer->backend->vram = proxyRenderer->logger->vram;
78 proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger->oam;
79 proxyRenderer->backend->cache = NULL;
80 }
81}
82
83static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
84 memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM);
85 memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM);
86 memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM);
87
88 mVideoLoggerRendererReset(proxyRenderer->logger);
89}
90
91void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
92 if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
93 return;
94 }
95 renderer->backend = video->renderer;
96 video->renderer = &renderer->d;
97 renderer->d.cache = renderer->backend->cache;
98 renderer->d.palette = video->palette;
99 renderer->d.vram = video->vram;
100 renderer->d.oam = &video->oam;
101 _init(renderer);
102 _reset(renderer);
103}
104
105void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
106 if (video->renderer != &renderer->d) {
107 return;
108 }
109 renderer->backend->cache = video->renderer->cache;
110 video->renderer = renderer->backend;
111 renderer->backend->palette = video->palette;
112 renderer->backend->vram = video->vram;
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 mVideoLoggerRendererFlush(proxyRenderer->logger);
150 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
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 STORE_16LE(item->value, item->address, logger->palette);
185 proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
186 }
187 break;
188 case DIRTY_OAM:
189 if (item->address < SIZE_OAM) {
190 STORE_16LE(item->value, item->address << 1, logger->oam);
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 proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0];
209 proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1];
210 proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN;
211 proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0];
212 proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1];
213 proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2];
214 proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3];
215 memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ));
216 proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount;
217 if (item->address < GBA_VIDEO_VERTICAL_PIXELS) {
218 proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
219 }
220 break;
221 case DIRTY_FRAME:
222 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
223 break;
224 case DIRTY_FLUSH:
225 return false;
226 default:
227 return false;
228 }
229 return true;
230}
231
232static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
233 struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
234 return &proxyRenderer->d.vram[address >> 1];
235}
236
237uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
238 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
239 switch (address) {
240 case REG_DISPCNT:
241 value &= 0xFFF7;
242 break;
243 case REG_BG0CNT:
244 case REG_BG1CNT:
245 value &= 0xDFFF;
246 break;
247 case REG_BG2CNT:
248 case REG_BG3CNT:
249 value &= 0xFFFF;
250 break;
251 case REG_BG0HOFS:
252 case REG_BG0VOFS:
253 case REG_BG1HOFS:
254 case REG_BG1VOFS:
255 case REG_BG2HOFS:
256 case REG_BG2VOFS:
257 case REG_BG3HOFS:
258 case REG_BG3VOFS:
259 value &= 0x01FF;
260 break;
261 }
262 if (address > REG_BLDY) {
263 return value;
264 }
265
266 mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
267 if (!proxyRenderer->logger->block) {
268 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
269 }
270 return value;
271}
272
273void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
274 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
275 mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
276 if (!proxyRenderer->logger->block) {
277 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
278 }
279 if (renderer->cache) {
280 mCacheSetWriteVRAM(renderer->cache, address);
281 }
282}
283
284void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
285 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
286 mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
287 if (!proxyRenderer->logger->block) {
288 proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
289 }
290 if (renderer->cache) {
291 mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
292 }
293}
294
295void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
296 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
297 if (!proxyRenderer->logger->block) {
298 proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
299 }
300 mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]);
301}
302
303void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
304 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
305 if (!proxyRenderer->logger->block) {
306 proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
307 proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
308 proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
309 proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
310 proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
311 proxyRenderer->backend->disableWIN[0] = proxyRenderer->d.disableWIN[0];
312 proxyRenderer->backend->disableWIN[1] = proxyRenderer->d.disableWIN[1];
313 proxyRenderer->backend->disableOBJWIN = proxyRenderer->d.disableOBJWIN;
314 proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0];
315 proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1];
316 proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2];
317 proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3];
318 proxyRenderer->backend->drawScanline(proxyRenderer->backend, y);
319 }
320 mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
321 if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
322 proxyRenderer->logger->wake(proxyRenderer->logger, y);
323 }
324}
325
326void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
327 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
328 if (!proxyRenderer->logger->block) {
329 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
330 }
331 mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
332 mVideoLoggerRendererFlush(proxyRenderer->logger);
333}
334
335static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
336 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
337 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
338 // Insert an extra item into the queue to make sure it gets flushed
339 mVideoLoggerRendererFlush(proxyRenderer->logger);
340 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
341 mVideoLoggerRendererFlush(proxyRenderer->logger);
342 *pixels = proxyRenderer->logger->pixelBuffer;
343 *stride = proxyRenderer->logger->pixelStride;
344 } else {
345 proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
346 }
347}
348
349static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
350 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
351 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
352 proxyRenderer->logger->lock(proxyRenderer->logger);
353 }
354 proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
355 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
356 proxyRenderer->logger->unlock(proxyRenderer->logger);
357 }
358}