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