src/gba/renderers/video-software.c (view raw)
1/* Copyright (c) 2013-2015 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 "gba/renderers/software-private.h"
7
8#include <mgba/core/tile-cache.h>
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/gba/io.h>
11
12#include <mgba-util/arm-algo.h>
13#include <mgba-util/memory.h>
14
15static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
16static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
17static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer);
18static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
19static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
20static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
21static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
22static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
23static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
24static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
25static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
26
27static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
28static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
29static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
30static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
31static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
32static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
33static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
34static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
35static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
36static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
37static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
38
39static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
40
41static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
42
43static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y);
44static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win);
45
46void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
47 renderer->d.init = GBAVideoSoftwareRendererInit;
48 renderer->d.reset = GBAVideoSoftwareRendererReset;
49 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
50 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
51 renderer->d.writeVRAM = GBAVideoSoftwareRendererWriteVRAM;
52 renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
53 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
54 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
55 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
56 renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels;
57 renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels;
58
59 renderer->d.disableBG[0] = false;
60 renderer->d.disableBG[1] = false;
61 renderer->d.disableBG[2] = false;
62 renderer->d.disableBG[3] = false;
63 renderer->d.disableOBJ = false;
64 renderer->tileStride = 0x20;
65 renderer->masterEnd = VIDEO_HORIZONTAL_PIXELS;
66
67 renderer->temporaryBuffer = 0;
68}
69
70static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
71 GBAVideoSoftwareRendererReset(renderer);
72
73 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
74
75 int y;
76 for (y = 0; y < softwareRenderer->masterEnd; ++y) {
77 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
78 int x;
79 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
80 row[x] = GBA_COLOR_WHITE;
81 }
82 }
83}
84
85static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
86 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
87 int i;
88
89 softwareRenderer->dispcnt = 0x0080;
90
91 softwareRenderer->target1Obj = 0;
92 softwareRenderer->target1Bd = 0;
93 softwareRenderer->target2Obj = 0;
94 softwareRenderer->target2Bd = 0;
95 softwareRenderer->blendEffect = BLEND_NONE;
96 for (i = 0; i < 1024; i += 2) {
97 uint16_t entry;
98 LOAD_16(entry, i, softwareRenderer->d.palette);
99 GBAVideoSoftwareRendererWritePalette(renderer, i, entry);
100 }
101 _updatePalettes(softwareRenderer);
102
103 softwareRenderer->blda = 0;
104 softwareRenderer->bldb = 0;
105 softwareRenderer->bldy = 0;
106
107 softwareRenderer->winN[0] = (struct WindowN) { .control = { .priority = 0 } };
108 softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } };
109 softwareRenderer->objwin = (struct WindowControl) { .priority = 2 };
110 softwareRenderer->winout = (struct WindowControl) { .priority = 3 };
111 softwareRenderer->oamMax = 0;
112
113 softwareRenderer->mosaic = 0;
114
115 for (i = 0; i < 4; ++i) {
116 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
117 bg->index = i;
118 bg->enabled = 0;
119 bg->priority = 0;
120 bg->charBase = 0;
121 bg->mosaic = 0;
122 bg->multipalette = 0;
123 bg->screenBase = 0;
124 bg->overflow = 0;
125 bg->size = 0;
126 bg->target1 = 0;
127 bg->target2 = 0;
128 bg->x = 0;
129 bg->y = 0;
130 bg->refx = 0;
131 bg->refy = 0;
132 bg->dx = 256;
133 bg->dmx = 0;
134 bg->dy = 0;
135 bg->dmy = 256;
136 bg->sx = 0;
137 bg->sy = 0;
138 bg->extPalette = NULL;
139 bg->variantPalette = NULL;
140 }
141}
142
143static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
144 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
145 UNUSED(softwareRenderer);
146}
147
148static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
149 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
150 switch (address) {
151 case REG_DISPCNT:
152 softwareRenderer->dispcnt = value;
153 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
154 break;
155 case REG_BG0CNT:
156 value &= 0xDFFF;
157 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
158 break;
159 case REG_BG1CNT:
160 value &= 0xDFFF;
161 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
162 break;
163 case REG_BG2CNT:
164 value &= 0xFFFF;
165 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
166 break;
167 case REG_BG3CNT:
168 value &= 0xFFFF;
169 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
170 break;
171 case REG_BG0HOFS:
172 value &= 0x01FF;
173 softwareRenderer->bg[0].x = value;
174 break;
175 case REG_BG0VOFS:
176 value &= 0x01FF;
177 softwareRenderer->bg[0].y = value;
178 break;
179 case REG_BG1HOFS:
180 value &= 0x01FF;
181 softwareRenderer->bg[1].x = value;
182 break;
183 case REG_BG1VOFS:
184 value &= 0x01FF;
185 softwareRenderer->bg[1].y = value;
186 break;
187 case REG_BG2HOFS:
188 value &= 0x01FF;
189 softwareRenderer->bg[2].x = value;
190 break;
191 case REG_BG2VOFS:
192 value &= 0x01FF;
193 softwareRenderer->bg[2].y = value;
194 break;
195 case REG_BG3HOFS:
196 value &= 0x01FF;
197 softwareRenderer->bg[3].x = value;
198 break;
199 case REG_BG3VOFS:
200 value &= 0x01FF;
201 softwareRenderer->bg[3].y = value;
202 break;
203 case REG_BG2PA:
204 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
205 break;
206 case REG_BG2PB:
207 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
208 break;
209 case REG_BG2PC:
210 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
211 break;
212 case REG_BG2PD:
213 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
214 break;
215 case REG_BG2X_LO:
216 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
217 break;
218 case REG_BG2X_HI:
219 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
220 break;
221 case REG_BG2Y_LO:
222 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
223 break;
224 case REG_BG2Y_HI:
225 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
226 break;
227 case REG_BG3PA:
228 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
229 break;
230 case REG_BG3PB:
231 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
232 break;
233 case REG_BG3PC:
234 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
235 break;
236 case REG_BG3PD:
237 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
238 break;
239 case REG_BG3X_LO:
240 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
241 break;
242 case REG_BG3X_HI:
243 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
244 break;
245 case REG_BG3Y_LO:
246 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
247 break;
248 case REG_BG3Y_HI:
249 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
250 break;
251 case REG_BLDCNT:
252 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
253 value &= 0x3FFF;
254 break;
255 case REG_BLDALPHA:
256 softwareRenderer->blda = value & 0x1F;
257 if (softwareRenderer->blda > 0x10) {
258 softwareRenderer->blda = 0x10;
259 }
260 softwareRenderer->bldb = (value >> 8) & 0x1F;
261 if (softwareRenderer->bldb > 0x10) {
262 softwareRenderer->bldb = 0x10;
263 }
264 value &= 0x1F1F;
265 break;
266 case REG_BLDY:
267 softwareRenderer->bldy = value & 0x1F;
268 if (softwareRenderer->bldy > 0x10) {
269 softwareRenderer->bldy = 0x10;
270 }
271 _updatePalettes(softwareRenderer);
272 break;
273 case REG_WIN0H:
274 softwareRenderer->winN[0].h.end = value;
275 softwareRenderer->winN[0].h.start = value >> 8;
276 if (softwareRenderer->winN[0].h.start > softwareRenderer->masterEnd && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
277 softwareRenderer->winN[0].h.start = 0;
278 }
279 if (softwareRenderer->winN[0].h.end > softwareRenderer->masterEnd) {
280 softwareRenderer->winN[0].h.end = softwareRenderer->masterEnd;
281 if (softwareRenderer->winN[0].h.start > softwareRenderer->masterEnd) {
282 softwareRenderer->winN[0].h.start = softwareRenderer->masterEnd;
283 }
284 }
285 break;
286 case REG_WIN1H:
287 softwareRenderer->winN[1].h.end = value;
288 softwareRenderer->winN[1].h.start = value >> 8;
289 if (softwareRenderer->winN[1].h.start > softwareRenderer->masterEnd && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
290 softwareRenderer->winN[1].h.start = 0;
291 }
292 if (softwareRenderer->winN[1].h.end > softwareRenderer->masterEnd) {
293 softwareRenderer->winN[1].h.end = softwareRenderer->masterEnd;
294 if (softwareRenderer->winN[1].h.start > softwareRenderer->masterEnd) {
295 softwareRenderer->winN[1].h.start = softwareRenderer->masterEnd;
296 }
297 }
298 break;
299 case REG_WIN0V:
300 softwareRenderer->winN[0].v.end = value;
301 softwareRenderer->winN[0].v.start = value >> 8;
302 if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
303 softwareRenderer->winN[0].v.start = 0;
304 }
305 if (softwareRenderer->winN[0].v.end > VIDEO_VERTICAL_PIXELS) {
306 softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
307 if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS) {
308 softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS;
309 }
310 }
311 break;
312 case REG_WIN1V:
313 softwareRenderer->winN[1].v.end = value;
314 softwareRenderer->winN[1].v.start = value >> 8;
315 if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
316 softwareRenderer->winN[1].v.start = 0;
317 }
318 if (softwareRenderer->winN[1].v.end > VIDEO_VERTICAL_PIXELS) {
319 softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
320 if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS) {
321 softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS;
322 }
323 }
324 break;
325 case REG_WININ:
326 value &= 0x3F3F;
327 softwareRenderer->winN[0].control.packed = value;
328 softwareRenderer->winN[1].control.packed = value >> 8;
329 break;
330 case REG_WINOUT:
331 value &= 0x3F3F;
332 softwareRenderer->winout.packed = value;
333 softwareRenderer->objwin.packed = value >> 8;
334 break;
335 case REG_MOSAIC:
336 softwareRenderer->mosaic = value;
337 break;
338 case REG_GREENSWP:
339 mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);
340 break;
341 default:
342 mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
343 }
344 return value;
345}
346
347static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
348 if (renderer->cache) {
349 mTileCacheWriteVRAM(renderer->cache, address);
350 }
351}
352
353static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
354 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
355 softwareRenderer->oamDirty = 1;
356 UNUSED(oam);
357}
358
359static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
360 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
361#ifdef COLOR_16_BIT
362#ifdef COLOR_5_6_5
363 unsigned color = 0;
364 color |= (value & 0x001F) << 11;
365 color |= (value & 0x03E0) << 1;
366 color |= (value & 0x7C00) >> 10;
367#else
368 unsigned color = value;
369#endif
370#else
371 unsigned color = 0;
372 color |= (value << 3) & 0xF8;
373 color |= (value << 6) & 0xF800;
374 color |= (value << 9) & 0xF80000;
375 color |= (color >> 5) & 0x070707;
376#endif
377 softwareRenderer->normalPalette[address >> 1] = color;
378 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
379 softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
380 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
381 softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
382 }
383 if (renderer->cache) {
384 mTileCacheWritePalette(renderer->cache, address);
385 }
386}
387
388static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {
389 if (win->v.end >= win->v.start) {
390 if (y >= win->v.end) {
391 return;
392 }
393 if (y < win->v.start) {
394 return;
395 }
396 } else if (y >= win->v.end && y < win->v.start) {
397 return;
398 }
399 if (win->h.end > softwareRenderer->masterEnd || win->h.end < win->h.start) {
400 struct WindowN splits[2] = { *win, *win };
401 splits[0].h.start = 0;
402 splits[1].h.end = softwareRenderer->masterEnd;
403 _breakWindowInner(softwareRenderer, &splits[0]);
404 _breakWindowInner(softwareRenderer, &splits[1]);
405 } else {
406 _breakWindowInner(softwareRenderer, win);
407 }
408}
409
410static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
411 int activeWindow;
412 int startX = 0;
413 if (win->h.end > 0) {
414 for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
415 if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
416 // Insert a window before the end of the active window
417 struct Window oldWindow = softwareRenderer->windows[activeWindow];
418 if (win->h.start > startX) {
419 // And after the start of the active window
420 int nextWindow = softwareRenderer->nWindows;
421 ++softwareRenderer->nWindows;
422 for (; nextWindow > activeWindow; --nextWindow) {
423 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
424 }
425 softwareRenderer->windows[activeWindow].endX = win->h.start;
426 ++activeWindow;
427 }
428 softwareRenderer->windows[activeWindow].control = win->control;
429 softwareRenderer->windows[activeWindow].endX = win->h.end;
430 if (win->h.end >= oldWindow.endX) {
431 // Trim off extra windows we've overwritten
432 for (++activeWindow; softwareRenderer->nWindows > activeWindow + 1 && win->h.end >= softwareRenderer->windows[activeWindow].endX; ++activeWindow) {
433 if (VIDEO_CHECKS && activeWindow >= MAX_WINDOW) {
434 mLOG(GBA_VIDEO, FATAL, "Out of bounds window write will occur");
435 return;
436 }
437 softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
438 --softwareRenderer->nWindows;
439 }
440 } else {
441 ++activeWindow;
442 int nextWindow = softwareRenderer->nWindows;
443 ++softwareRenderer->nWindows;
444 for (; nextWindow > activeWindow; --nextWindow) {
445 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
446 }
447 softwareRenderer->windows[activeWindow] = oldWindow;
448 }
449 break;
450 }
451 startX = softwareRenderer->windows[activeWindow].endX;
452 }
453 }
454#ifdef DEBUG
455 if (softwareRenderer->nWindows > MAX_WINDOW) {
456 mLOG(GBA_VIDEO, FATAL, "Out of bounds window write occurred!");
457 }
458#endif
459}
460
461static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
462 int i;
463 int oamMax = 0;
464 for (i = 0; i < 128; ++i) {
465 struct GBAObj obj;
466 LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a);
467 LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b);
468 LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c);
469 if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
470 int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(obj.a) * 4 + GBAObjAttributesBGetSize(obj.b)][1];
471 if (GBAObjAttributesAIsTransformed(obj.a)) {
472 height <<= GBAObjAttributesAGetDoubleSize(obj.a);
473 }
474 if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
475 renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a);
476 renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height;
477 renderer->sprites[oamMax].obj = obj;
478 ++oamMax;
479 }
480 }
481 }
482 renderer->oamMax = oamMax;
483 renderer->oamDirty = 0;
484}
485
486static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
487 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
488
489 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
490 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
491 int x;
492 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
493 row[x] = GBA_COLOR_WHITE;
494 }
495 return;
496 }
497
498 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
499 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
500
501 int w;
502 unsigned priority;
503 for (priority = 0; priority < 4; ++priority) {
504 softwareRenderer->end = 0;
505 for (w = 0; w < softwareRenderer->nWindows; ++w) {
506 softwareRenderer->start = softwareRenderer->end;
507 softwareRenderer->end = softwareRenderer->windows[w].endX;
508 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
509 if (spriteLayers & (1 << priority)) {
510 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
511 }
512 if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
513 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
514 }
515 if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
516 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
517 }
518 if (TEST_LAYER_ENABLED(2)) {
519 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
520 case 0:
521 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
522 break;
523 case 1:
524 case 2:
525 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
526 break;
527 case 3:
528 GBAVideoSoftwareRendererDrawBackgroundMode3(softwareRenderer, &softwareRenderer->bg[2], y);
529 break;
530 case 4:
531 GBAVideoSoftwareRendererDrawBackgroundMode4(softwareRenderer, &softwareRenderer->bg[2], y);
532 break;
533 case 5:
534 GBAVideoSoftwareRendererDrawBackgroundMode5(softwareRenderer, &softwareRenderer->bg[2], y);
535 break;
536 }
537 }
538 if (TEST_LAYER_ENABLED(3)) {
539 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
540 case 0:
541 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
542 break;
543 case 2:
544 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
545 break;
546 }
547 }
548 }
549 }
550 softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
551 softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
552 softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
553 softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
554
555 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
556
557#ifdef COLOR_16_BIT
558#if defined(__ARM_NEON) && !defined(__APPLE__)
559 _to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
560#else
561 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
562 row[x] = softwareRenderer->row[x];
563 }
564#endif
565#else
566 memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
567#endif
568}
569
570static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
571 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
572
573 if (softwareRenderer->temporaryBuffer) {
574 mappedMemoryFree(softwareRenderer->temporaryBuffer, softwareRenderer->masterEnd * VIDEO_VERTICAL_PIXELS * 4);
575 softwareRenderer->temporaryBuffer = 0;
576 }
577 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
578 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
579 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
580 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
581}
582
583static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
584 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
585 *stride = softwareRenderer->outputBufferStride;
586 *pixels = softwareRenderer->outputBuffer;
587}
588
589static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
590 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
591
592 const color_t* colorPixels = pixels;
593 unsigned i;
594 for (i = 0; i < VIDEO_VERTICAL_PIXELS; ++i) {
595 memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], softwareRenderer->masterEnd * BYTES_PER_PIXEL);
596 }
597}
598
599static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
600 renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt) && !renderer->d.disableBG[0];
601 renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt) && !renderer->d.disableBG[1];
602 renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt) && !renderer->d.disableBG[2];
603 renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt) && !renderer->d.disableBG[3];
604}
605
606static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
607 UNUSED(renderer);
608 bg->priority = GBARegisterBGCNTGetPriority(value);
609 bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14;
610 if (!renderer->d.vramBG[4]) {
611 bg->charBase &= 0xC000;
612 }
613 bg->mosaic = GBARegisterBGCNTGetMosaic(value);
614 bg->multipalette = GBARegisterBGCNTGet256Color(value);
615 bg->screenBase &= ~0xF800;
616 bg->screenBase |= GBARegisterBGCNTGetScreenBase(value) << 11;
617 bg->overflow = GBARegisterBGCNTGetOverflow(value);
618 bg->size = GBARegisterBGCNTGetSize(value);
619 bg->control = value;
620}
621
622static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
623 bg->dx = value;
624}
625
626static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
627 bg->dmx = value;
628}
629
630static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
631 bg->dy = value;
632}
633
634static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
635 bg->dmy = value;
636}
637
638static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
639 bg->refx = (bg->refx & 0xFFFF0000) | value;
640 bg->sx = bg->refx;
641}
642
643static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
644 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
645 bg->refx <<= 4;
646 bg->refx >>= 4;
647 bg->sx = bg->refx;
648}
649
650static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
651 bg->refy = (bg->refy & 0xFFFF0000) | value;
652 bg->sy = bg->refy;
653}
654
655static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
656 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
657 bg->refy <<= 4;
658 bg->refy >>= 4;
659 bg->sy = bg->refy;
660}
661
662static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
663 enum BlendEffect oldEffect = renderer->blendEffect;
664
665 renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
666 renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
667 renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
668 renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
669 renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
670 renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
671 renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
672 renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
673
674 renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
675 renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
676 renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
677 renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
678 renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
679
680 if (oldEffect != renderer->blendEffect) {
681 _updatePalettes(renderer);
682 }
683}
684
685void GBAVideoSoftwareRendererPreprocessBuffer(struct GBAVideoSoftwareRenderer* softwareRenderer, int y) {
686 int x;
687 int masterEnd = softwareRenderer->masterEnd;
688 for (x = 0; x < masterEnd; x += 4) {
689 softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
690 softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
691 softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
692 softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
693 }
694
695 softwareRenderer->windows[0].endX = softwareRenderer->masterEnd;
696 softwareRenderer->nWindows = 1;
697 if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
698 softwareRenderer->windows[0].control = softwareRenderer->winout;
699 if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt)) {
700 _breakWindow(softwareRenderer, &softwareRenderer->winN[1], y);
701 }
702 if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt)) {
703 _breakWindow(softwareRenderer, &softwareRenderer->winN[0], y);
704 }
705 } else {
706 softwareRenderer->windows[0].control.packed = 0xFF;
707 }
708
709 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
710
711 int w;
712 x = 0;
713 for (w = 0; w < softwareRenderer->nWindows; ++w) {
714 // TOOD: handle objwin on backdrop
715 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
716 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
717 backdrop |= softwareRenderer->normalPalette[0];
718 } else {
719 backdrop |= softwareRenderer->variantPalette[0];
720 }
721 int end = softwareRenderer->windows[w].endX;
722 for (; x & 3; ++x) {
723 softwareRenderer->row[x] = backdrop;
724 }
725 for (; x < end - 3; x += 4) {
726 softwareRenderer->row[x] = backdrop;
727 softwareRenderer->row[x + 1] = backdrop;
728 softwareRenderer->row[x + 2] = backdrop;
729 softwareRenderer->row[x + 3] = backdrop;
730 }
731 for (; x < end; ++x) {
732 softwareRenderer->row[x] = backdrop;
733 }
734 }
735}
736
737void GBAVideoSoftwareRendererPostprocessBuffer(struct GBAVideoSoftwareRenderer* softwareRenderer) {
738 int x, w;
739 if (softwareRenderer->target2Bd) {
740 x = 0;
741 for (w = 0; w < softwareRenderer->nWindows; ++w) {
742 uint32_t backdrop = 0;
743 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
744 backdrop |= softwareRenderer->normalPalette[0];
745 } else {
746 backdrop |= softwareRenderer->variantPalette[0];
747 }
748 int end = softwareRenderer->windows[w].endX;
749 for (; x < end; ++x) {
750 uint32_t color = softwareRenderer->row[x];
751 if (color & FLAG_TARGET_1) {
752 softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
753 }
754 }
755 }
756 }
757 if (softwareRenderer->target1Obj && (softwareRenderer->blendEffect == BLEND_DARKEN || softwareRenderer->blendEffect == BLEND_BRIGHTEN)) {
758 x = 0;
759 for (w = 0; w < softwareRenderer->nWindows; ++w) {
760 if (!GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
761 continue;
762 }
763 int end = softwareRenderer->windows[w].endX;
764 if (softwareRenderer->blendEffect == BLEND_DARKEN) {
765 for (; x < end; ++x) {
766 uint32_t color = softwareRenderer->row[x];
767 if ((color & 0xFF000000) == FLAG_REBLEND) {
768 softwareRenderer->row[x] = _darken(color, softwareRenderer->bldy);
769 }
770 }
771 } else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
772 for (; x < end; ++x) {
773 uint32_t color = softwareRenderer->row[x];
774 if ((color & 0xFF000000) == FLAG_REBLEND) {
775 softwareRenderer->row[x] = _brighten(color, softwareRenderer->bldy);
776 }
777 }
778 }
779 }
780 }
781}
782
783int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRenderer* renderer, int y) {
784 int w;
785 renderer->end = 0;
786 int spriteLayers = 0;
787 if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt) && !renderer->d.disableOBJ) {
788 if (renderer->oamDirty) {
789 _cleanOAM(renderer);
790 }
791 renderer->spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(renderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH;
792 int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
793 int mosaicY = y - (y % mosaicV);
794 for (w = 0; w < renderer->nWindows; ++w) {
795 renderer->start = renderer->end;
796 renderer->end = renderer->windows[w].endX;
797 renderer->currentWindow = renderer->windows[w].control;
798 if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
799 continue;
800 }
801 int i;
802 int drawn;
803
804 for (i = 0; i < renderer->oamMax; ++i) {
805 int localY = y;
806 if (renderer->spriteCyclesRemaining <= 0) {
807 break;
808 }
809 struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
810 if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
811 localY = mosaicY;
812 }
813 if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
814 continue;
815 }
816 drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY);
817 spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
818 }
819 }
820 }
821 return spriteLayers;
822}
823
824static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
825 int i;
826 if (renderer->blendEffect == BLEND_BRIGHTEN) {
827 for (i = 0; i < 512; ++i) {
828 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
829 }
830 } else if (renderer->blendEffect == BLEND_DARKEN) {
831 for (i = 0; i < 512; ++i) {
832 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
833 }
834 } else {
835 for (i = 0; i < 512; ++i) {
836 renderer->variantPalette[i] = renderer->normalPalette[i];
837 }
838 }
839}