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}