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