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