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