src/gba/renderers/video-software.c (view raw)
1#include "video-software.h"
2
3#include "gba.h"
4#include "gba-io.h"
5
6#include <string.h>
7
8static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
9static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
10static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
11static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
12static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
13static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
14static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
15
16static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
17static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
18static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
19static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
20static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
21static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
22static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
23static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
24static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
25static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
26static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
27
28static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
29static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
30static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
31static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
32static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
33static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
34static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
35static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
36static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
37
38static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
39static inline color_t _brighten(color_t color, int y);
40static inline color_t _darken(color_t color, int y);
41static color_t _mix(int weightA, color_t colorA, int weightB, color_t colorB);
42
43void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
44 renderer->d.init = GBAVideoSoftwareRendererInit;
45 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
46 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
47 renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
48 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
49 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
50 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
51}
52
53static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
54 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
55 int i;
56
57 softwareRenderer->dispcnt.packed = 0x0080;
58
59 softwareRenderer->target1Obj = 0;
60 softwareRenderer->target1Bd = 0;
61 softwareRenderer->target2Obj = 0;
62 softwareRenderer->target2Bd = 0;
63 softwareRenderer->blendEffect = BLEND_NONE;
64 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
65 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
66
67 softwareRenderer->blda = 0;
68 softwareRenderer->bldb = 0;
69 softwareRenderer->bldy = 0;
70
71 softwareRenderer->winN[0].control.priority = 0;
72 softwareRenderer->winN[1].control.priority = 1;
73 softwareRenderer->objwin.priority = 2;
74 softwareRenderer->winout.priority = 3;
75
76 for (i = 0; i < 4; ++i) {
77 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
78 bg->index = i;
79 bg->enabled = 0;
80 bg->priority = 0;
81 bg->charBase = 0;
82 bg->mosaic = 0;
83 bg->multipalette = 0;
84 bg->screenBase = 0;
85 bg->overflow = 0;
86 bg->size = 0;
87 bg->target1 = 0;
88 bg->target2 = 0;
89 bg->x = 0;
90 bg->y = 0;
91 bg->refx = 0;
92 bg->refy = 0;
93 bg->dx = 256;
94 bg->dmx = 0;
95 bg->dy = 0;
96 bg->dmy = 256;
97 bg->sx = 0;
98 bg->sy = 0;
99 }
100}
101
102static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
103 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
104}
105
106static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
107 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
108 switch (address) {
109 case REG_DISPCNT:
110 softwareRenderer->dispcnt.packed = value;
111 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
112 break;
113 case REG_BG0CNT:
114 value &= 0xFFCF;
115 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
116 break;
117 case REG_BG1CNT:
118 value &= 0xFFCF;
119 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
120 break;
121 case REG_BG2CNT:
122 value &= 0xFFCF;
123 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
124 break;
125 case REG_BG3CNT:
126 value &= 0xFFCF;
127 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
128 break;
129 case REG_BG0HOFS:
130 value &= 0x01FF;
131 softwareRenderer->bg[0].x = value;
132 break;
133 case REG_BG0VOFS:
134 value &= 0x01FF;
135 softwareRenderer->bg[0].y = value;
136 break;
137 case REG_BG1HOFS:
138 value &= 0x01FF;
139 softwareRenderer->bg[1].x = value;
140 break;
141 case REG_BG1VOFS:
142 value &= 0x01FF;
143 softwareRenderer->bg[1].y = value;
144 break;
145 case REG_BG2HOFS:
146 value &= 0x01FF;
147 softwareRenderer->bg[2].x = value;
148 break;
149 case REG_BG2VOFS:
150 value &= 0x01FF;
151 softwareRenderer->bg[2].y = value;
152 break;
153 case REG_BG3HOFS:
154 value &= 0x01FF;
155 softwareRenderer->bg[3].x = value;
156 break;
157 case REG_BG3VOFS:
158 value &= 0x01FF;
159 softwareRenderer->bg[3].y = value;
160 break;
161 case REG_BG2PA:
162 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
163 break;
164 case REG_BG2PB:
165 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
166 break;
167 case REG_BG2PC:
168 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
169 break;
170 case REG_BG2PD:
171 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
172 break;
173 case REG_BG2X_LO:
174 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
175 break;
176 case REG_BG2X_HI:
177 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
178 break;
179 case REG_BG2Y_LO:
180 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
181 break;
182 case REG_BG2Y_HI:
183 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
184 break;
185 case REG_BG3PA:
186 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
187 break;
188 case REG_BG3PB:
189 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
190 break;
191 case REG_BG3PC:
192 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
193 break;
194 case REG_BG3PD:
195 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
196 break;
197 case REG_BG3X_LO:
198 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
199 break;
200 case REG_BG3X_HI:
201 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
202 break;
203 case REG_BG3Y_LO:
204 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
205 break;
206 case REG_BG3Y_HI:
207 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
208 break;
209 case REG_BLDCNT:
210 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
211 break;
212 case REG_BLDALPHA:
213 softwareRenderer->blda = value & 0x1F;
214 if (softwareRenderer->blda > 0x10) {
215 softwareRenderer->blda = 0x10;
216 }
217 softwareRenderer->bldb = (value >> 8) & 0x1F;
218 if (softwareRenderer->bldb > 0x10) {
219 softwareRenderer->bldb = 0x10;
220 }
221 break;
222 case REG_BLDY:
223 softwareRenderer->bldy = value & 0x1F;
224 if (softwareRenderer->bldy > 0x10) {
225 softwareRenderer->bldy = 0x10;
226 }
227 _updatePalettes(softwareRenderer);
228 break;
229 case REG_WIN0H:
230 softwareRenderer->winN[0].h.packed = value;
231 if (softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end || softwareRenderer->winN[0].h.end > VIDEO_HORIZONTAL_PIXELS) {
232 softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS;
233 }
234 break;
235 case REG_WIN1H:
236 softwareRenderer->winN[1].h.packed = value;
237 if (softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end || softwareRenderer->winN[1].h.end > VIDEO_HORIZONTAL_PIXELS) {
238 softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS;
239 }
240 break;
241 case REG_WIN0V:
242 softwareRenderer->winN[0].v.packed = value;
243 if (softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end || softwareRenderer->winN[0].v.end > VIDEO_HORIZONTAL_PIXELS) {
244 softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
245 }
246 break;
247 case REG_WIN1V:
248 softwareRenderer->winN[1].v.packed = value;
249 if (softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end || softwareRenderer->winN[1].v.end > VIDEO_HORIZONTAL_PIXELS) {
250 softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
251 }
252 break;
253 case REG_WININ:
254 softwareRenderer->winN[0].control.packed = value;
255 softwareRenderer->winN[1].control.packed = value >> 8;
256 break;
257 case REG_WINOUT:
258 softwareRenderer->winout.packed = value;
259 softwareRenderer->objwin.packed = value >> 8;
260 break;
261 case REG_MOSAIC:
262 softwareRenderer->mosaic.packed = value;
263 break;
264 case REG_GREENSWP:
265 GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);
266 break;
267 default:
268 GBALog(0, GBA_LOG_GAME_ERROR, "Invalid video register: 0x%03X", address);
269 }
270 return value;
271}
272
273static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
274 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
275}
276
277static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
278 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
279#ifdef COLOR_16_BIT
280 color_t color = value;
281#else
282 color_t color = 0;
283 color |= (value << 3) & 0xF8;
284 color |= (value << 6) & 0xF800;
285 color |= (value << 9) & 0xF80000;
286#endif
287 softwareRenderer->normalPalette[address >> 1] = color;
288 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
289 softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
290 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
291 softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
292 }
293}
294
295static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
296 int activeWindow;
297 int startX = 0;
298 if (win->h.end > 0) {
299 for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
300 if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
301 // Insert a window before the end of the active window
302 struct Window oldWindow = softwareRenderer->windows[activeWindow];
303 if (win->h.start > startX) {
304 // And after the start of the active window
305 int nextWindow = softwareRenderer->nWindows;
306 ++softwareRenderer->nWindows;
307 for (; nextWindow > activeWindow; --nextWindow) {
308 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
309 }
310 softwareRenderer->windows[activeWindow].endX = win->h.start;
311 ++activeWindow;
312 }
313 softwareRenderer->windows[activeWindow].control = win->control;
314 softwareRenderer->windows[activeWindow].endX = win->h.end;
315 if (win->h.end >= oldWindow.endX) {
316 // Trim off extra windows we've overwritten
317 for (++activeWindow; win->h.end >= softwareRenderer->windows[activeWindow].endX && softwareRenderer->nWindows > activeWindow; ++activeWindow) {
318 softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
319 --softwareRenderer->nWindows;
320 }
321 } else {
322 ++activeWindow;
323 int nextWindow = softwareRenderer->nWindows;
324 ++softwareRenderer->nWindows;
325 for (; nextWindow > activeWindow; --nextWindow) {
326 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
327 }
328 softwareRenderer->windows[activeWindow] = oldWindow;
329 }
330 break;
331 }
332 startX = softwareRenderer->windows[activeWindow].endX;
333 }
334 }
335}
336
337static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
338 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
339
340 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
341 if (softwareRenderer->dispcnt.forcedBlank) {
342 int x;
343 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
344 row[x] = GBA_COLOR_WHITE;
345 }
346 return;
347 }
348
349 memset(softwareRenderer->spriteLayer, 0, sizeof(softwareRenderer->spriteLayer));
350
351 softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
352 softwareRenderer->nWindows = 1;
353 if (softwareRenderer->dispcnt.win0Enable || softwareRenderer->dispcnt.win1Enable || softwareRenderer->dispcnt.objwinEnable) {
354 softwareRenderer->windows[0].control = softwareRenderer->winout;
355 if (softwareRenderer->dispcnt.win1Enable && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) {
356 _breakWindow(softwareRenderer, &softwareRenderer->winN[1]);
357 }
358 if (softwareRenderer->dispcnt.win0Enable && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) {
359 _breakWindow(softwareRenderer, &softwareRenderer->winN[0]);
360 }
361 } else {
362 softwareRenderer->windows[0].control.packed = 0xFF;
363 }
364
365 int w;
366 int x = 0;
367 for (w = 0; w < softwareRenderer->nWindows; ++w) {
368 // TOOD: handle objwin on backdrop
369 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
370 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
371 backdrop |= softwareRenderer->normalPalette[0];
372 } else {
373 backdrop |= softwareRenderer->variantPalette[0];
374 }
375 int end = softwareRenderer->windows[w].endX;
376 for (; x < end; ++x) {
377 softwareRenderer->row[x] = backdrop;
378 }
379 }
380
381 _drawScanline(softwareRenderer, y);
382
383 if (softwareRenderer->target2Bd) {
384 x = 0;
385 for (w = 0; w < softwareRenderer->nWindows; ++w) {
386 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
387 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
388 backdrop |= softwareRenderer->normalPalette[0];
389 } else {
390 backdrop |= softwareRenderer->variantPalette[0];
391 }
392 int end = softwareRenderer->windows[w].endX;
393 for (; x < end; ++x) {
394 uint32_t color = softwareRenderer->row[x];
395 if (color & FLAG_TARGET_1 && !(color & FLAG_FINALIZED)) {
396 softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
397 }
398 }
399 }
400 }
401
402#ifdef COLOR_16_BIT
403 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
404 uint32_t c = softwareRenderer->row[x];
405#ifdef COLOR_5_6_5
406 c = ((c & 0x001F) << 11) | ((c & 0x03E0) << 1) | ((c & 0x7C00) >> 10);
407#endif
408 row[x] = c;
409 }
410#else
411 memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
412#endif
413}
414
415static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
416 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
417
418 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
419 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
420 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
421 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
422}
423
424static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
425 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
426 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
427 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
428 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
429}
430
431static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
432 (void)(renderer);
433 union GBARegisterBGCNT reg = { .packed = value };
434 bg->priority = reg.priority;
435 bg->charBase = reg.charBase << 14;
436 bg->mosaic = reg.mosaic;
437 bg->multipalette = reg.multipalette;
438 bg->screenBase = reg.screenBase << 11;
439 bg->overflow = reg.overflow;
440 bg->size = reg.size;
441}
442
443static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
444 bg->dx = value;
445}
446
447static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
448 bg->dmx = value;
449}
450
451static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
452 bg->dy = value;
453}
454
455static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
456 bg->dmy = value;
457}
458
459static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
460 bg->refx = (bg->refx & 0xFFFF0000) | value;
461 bg->sx = bg->refx;
462}
463
464static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
465 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
466 bg->refx <<= 4;
467 bg->refx >>= 4;
468 bg->sx = bg->refx;
469}
470
471static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
472 bg->refy = (bg->refy & 0xFFFF0000) | value;
473 bg->sy = bg->refy;
474}
475
476static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
477 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
478 bg->refy <<= 4;
479 bg->refy >>= 4;
480 bg->sy = bg->refy;
481}
482
483static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
484 union {
485 struct {
486 unsigned target1Bg0 : 1;
487 unsigned target1Bg1 : 1;
488 unsigned target1Bg2 : 1;
489 unsigned target1Bg3 : 1;
490 unsigned target1Obj : 1;
491 unsigned target1Bd : 1;
492 enum BlendEffect effect : 2;
493 unsigned target2Bg0 : 1;
494 unsigned target2Bg1 : 1;
495 unsigned target2Bg2 : 1;
496 unsigned target2Bg3 : 1;
497 unsigned target2Obj : 1;
498 unsigned target2Bd : 1;
499 };
500 uint16_t packed;
501 } bldcnt = { .packed = value };
502
503 enum BlendEffect oldEffect = renderer->blendEffect;
504
505 renderer->bg[0].target1 = bldcnt.target1Bg0;
506 renderer->bg[1].target1 = bldcnt.target1Bg1;
507 renderer->bg[2].target1 = bldcnt.target1Bg2;
508 renderer->bg[3].target1 = bldcnt.target1Bg3;
509 renderer->bg[0].target2 = bldcnt.target2Bg0;
510 renderer->bg[1].target2 = bldcnt.target2Bg1;
511 renderer->bg[2].target2 = bldcnt.target2Bg2;
512 renderer->bg[3].target2 = bldcnt.target2Bg3;
513
514 renderer->blendEffect = bldcnt.effect;
515 renderer->target1Obj = bldcnt.target1Obj;
516 renderer->target1Bd = bldcnt.target1Bd;
517 renderer->target2Obj = bldcnt.target2Obj;
518 renderer->target2Bd = bldcnt.target2Bd;
519
520 if (oldEffect != renderer->blendEffect) {
521 _updatePalettes(renderer);
522 }
523}
524
525#define TEST_LAYER_ENABLED(X) \
526 (renderer->bg[X].enabled && \
527 (renderer->currentWindow.bg ## X ## Enable || \
528 (renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
529 renderer->bg[X].priority == priority)
530
531static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
532 int w;
533 renderer->end = 0;
534 int spriteLayers = 0;
535 if (renderer->dispcnt.objEnable) {
536 for (w = 0; w < renderer->nWindows; ++w) {
537 renderer->start = renderer->end;
538 renderer->end = renderer->windows[w].endX;
539 renderer->currentWindow = renderer->windows[w].control;
540 if (!renderer->currentWindow.objEnable) {
541 continue;
542 }
543 int i;
544 for (i = 0; i < 128; ++i) {
545 struct GBAObj* sprite = &renderer->d.oam->obj[i];
546 if (sprite->transformed) {
547 spriteLayers |= 1 << sprite->priority;
548 _preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
549 } else if (!sprite->disable) {
550 spriteLayers |= 1 << sprite->priority;
551 _preprocessSprite(renderer, sprite, y);
552 }
553 }
554 }
555 }
556
557 int priority;
558 for (priority = 0; priority < 4; ++priority) {
559 if (spriteLayers & (1 << priority)) {
560 _postprocessSprite(renderer, priority);
561 }
562 renderer->end = 0;
563 for (w = 0; w < renderer->nWindows; ++w) {
564 renderer->start = renderer->end;
565 renderer->end = renderer->windows[w].endX;
566 renderer->currentWindow = renderer->windows[w].control;
567 if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
568 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
569 }
570 if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
571 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
572 }
573 if (TEST_LAYER_ENABLED(2)) {
574 switch (renderer->dispcnt.mode) {
575 case 0:
576 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
577 break;
578 case 1:
579 case 2:
580 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
581 break;
582 case 3:
583 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
584 break;
585 case 4:
586 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
587 break;
588 case 5:
589 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
590 break;
591 }
592 }
593 if (TEST_LAYER_ENABLED(3)) {
594 switch (renderer->dispcnt.mode) {
595 case 0:
596 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
597 break;
598 case 2:
599 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
600 break;
601 }
602 }
603 }
604 }
605 renderer->bg[2].sx += renderer->bg[2].dmx;
606 renderer->bg[2].sy += renderer->bg[2].dmy;
607 renderer->bg[3].sx += renderer->bg[3].dmx;
608 renderer->bg[3].sy += renderer->bg[3].dmy;
609}
610
611static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color, uint32_t current) {
612 // We stash the priority on the top bits so we can do a one-operator comparison
613 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
614 // We want to do special processing if the color pixel is target 1, however
615 if ((color & FLAG_ORDER_MASK) < (current & FLAG_ORDER_MASK)) {
616 if (current & FLAG_UNWRITTEN) {
617 renderer->row[offset] = color | (current & FLAG_OBJWIN);
618 } else if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
619 renderer->row[offset] = color | FLAG_FINALIZED;
620 } else {
621 renderer->row[offset] = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
622 }
623 } else {
624 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
625 renderer->row[offset] = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
626 } else {
627 renderer->row[offset] = current | FLAG_FINALIZED;
628 }
629 }
630}
631
632#define BACKGROUND_DRAW_PIXEL_16 \
633 pixelData = tileData & 0xF; \
634 current = renderer->row[outX]; \
635 if (pixelData && !(current & FLAG_FINALIZED)) { \
636 if (!objwinSlowPath) { \
637 _composite(renderer, outX, palette[pixelData | paletteData] | flags, current); \
638 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
639 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette; \
640 _composite(renderer, outX, currentPalette[pixelData | paletteData] | flags, current); \
641 } \
642 } \
643 tileData >>= 4;
644
645#define BACKGROUND_DRAW_PIXEL_256 \
646 pixelData = tileData & 0xFF; \
647 current = renderer->row[outX]; \
648 if (pixelData && !(current & FLAG_FINALIZED)) { \
649 if (!objwinSlowPath) { \
650 _composite(renderer, outX, palette[pixelData] | flags, current); \
651 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
652 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette; \
653 _composite(renderer, outX, currentPalette[pixelData] | flags, current); \
654 } \
655 } \
656 tileData >>= 8;
657
658#define BACKGROUND_TEXT_SELECT_CHARACTER \
659 localX = tileX * 8 + inX; \
660 xBase = localX & 0xF8; \
661 if (background->size & 1) { \
662 xBase += (localX & 0x100) << 5; \
663 } \
664 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
665 mapData.packed = renderer->d.vram[screenBase]; \
666 if (!mapData.vflip) { \
667 localY = inY & 0x7; \
668 } else { \
669 localY = 7 - (inY & 0x7); \
670 }
671
672#define PREPARE_OBJWIN \
673 int objwinSlowPath = renderer->dispcnt.objwinEnable; \
674 int objwinOnly = 0; \
675 int objwinForceEnable = 0; \
676 color_t* objwinPalette; \
677 if (objwinSlowPath) { \
678 if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
679 objwinPalette = renderer->variantPalette; \
680 } else { \
681 objwinPalette = renderer->normalPalette; \
682 } \
683 switch (background->index) { \
684 case 0: \
685 objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \
686 objwinOnly = !renderer->objwin.bg0Enable; \
687 break; \
688 case 1: \
689 objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \
690 objwinOnly = !renderer->objwin.bg1Enable; \
691 break; \
692 case 2: \
693 objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \
694 objwinOnly = !renderer->objwin.bg2Enable; \
695 break; \
696 case 3: \
697 objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \
698 objwinOnly = !renderer->objwin.bg3Enable; \
699 break; \
700 } \
701 }
702
703static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
704 int inX = renderer->start + background->x;
705 if (background->mosaic) {
706 int mosaicV = renderer->mosaic.bgV + 1;
707 y -= y % mosaicV;
708 }
709 int inY = y + background->y;
710 union GBATextMapData mapData;
711
712 unsigned yBase = inY & 0xF8;
713 if (background->size == 2) {
714 yBase += inY & 0x100;
715 } else if (background->size == 3) {
716 yBase += (inY & 0x100) << 1;
717 }
718
719 int localX;
720 int localY;
721
722 unsigned xBase;
723
724 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
725 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
726 flags |= FLAG_TARGET_2 * background->target2;
727
728 uint32_t screenBase;
729 uint32_t charBase;
730 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
731 color_t* palette = renderer->normalPalette;
732 if (variant) {
733 palette = renderer->variantPalette;
734 }
735 PREPARE_OBJWIN;
736
737 int outX = renderer->start;
738
739 uint32_t tileData;
740 uint32_t current;
741 int pixelData;
742 int paletteData;
743 int tileX = 0;
744 int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
745
746 if (inX & 0x7) {
747 int mod8 = inX & 0x7;
748 BACKGROUND_TEXT_SELECT_CHARACTER;
749
750 int end = outX + 0x8 - mod8;
751 if (!background->multipalette) {
752 paletteData = mapData.palette << 4;
753 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
754 tileData = ((uint32_t*)renderer->d.vram)[charBase];
755 if (!mapData.hflip) {
756 tileData >>= 4 * mod8;
757 for (; outX < end; ++outX) {
758 BACKGROUND_DRAW_PIXEL_16;
759 }
760 } else {
761 for (outX = end - 1; outX >= renderer->start; --outX) {
762 BACKGROUND_DRAW_PIXEL_16;
763 }
764 }
765 } else {
766 // TODO: hflip
767 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
768 int end2 = end - 4;
769 int shift = inX & 0x3;
770 if (end2 > 0) {
771 tileData = ((uint32_t*)renderer->d.vram)[charBase];
772 tileData >>= 8 * shift;
773 shift = 0;
774 for (; outX < end2; ++outX) {
775 BACKGROUND_DRAW_PIXEL_256;
776 }
777 }
778
779 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
780 tileData >>= 8 * shift;
781 for (; outX < end; ++outX) {
782 BACKGROUND_DRAW_PIXEL_256;
783 }
784 }
785 }
786 if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) {
787 tileX = tileEnd;
788 int pixelData, paletteData;
789 int mod8 = (inX + renderer->end - renderer->start) & 0x7;
790 BACKGROUND_TEXT_SELECT_CHARACTER;
791
792 int end = 0x8 - mod8;
793 if (!background->multipalette) {
794 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
795 tileData = ((uint32_t*)renderer->d.vram)[charBase];
796 paletteData = mapData.palette << 4;
797 if (!mapData.hflip) {
798 outX = renderer->end - mod8;
799 if (outX < 0) {
800 tileData >>= 4 * -outX;
801 outX = 0;
802 }
803 for (; outX < renderer->end; ++outX) {
804 BACKGROUND_DRAW_PIXEL_16;
805 }
806 } else {
807 tileData >>= 4 * (0x8 - mod8);
808 int end2 = renderer->end - 8;
809 if (end2 < -1) {
810 end2 = -1;
811 }
812 for (outX = renderer->end - 1; outX > end2; --outX) {
813 BACKGROUND_DRAW_PIXEL_16;
814 }
815 }
816 } else {
817 // TODO: hflip
818 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
819 outX = renderer->end - 8 + end;
820 int end2 = 4 - end;
821 if (end2 > 0) {
822 tileData = ((uint32_t*)renderer->d.vram)[charBase];
823 for (; outX < renderer->end - end2; ++outX) {
824 BACKGROUND_DRAW_PIXEL_256;
825 }
826 ++charBase;
827 }
828
829 tileData = ((uint32_t*)renderer->d.vram)[charBase];
830 for (; outX < renderer->end; ++outX) {
831 BACKGROUND_DRAW_PIXEL_256;
832 }
833 }
834
835 tileX = (inX & 0x7) != 0;
836 outX = renderer->start + tileX * 8 - (inX & 0x7);
837 }
838
839 if (background->mosaic) {
840 int mosaicH = renderer->mosaic.bgH + 1;
841 int x;
842 int mosaicWait = outX % mosaicH;
843 int carryData = 0;
844
845 if (!background->multipalette) {
846 for (; tileX < tileEnd; ++tileX) {
847 BACKGROUND_TEXT_SELECT_CHARACTER;
848 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
849 tileData = carryData;
850 for (x = 0; x < 8; ++x) {
851 if (!mosaicWait) {
852 paletteData = mapData.palette << 4;
853 tileData = ((uint32_t*)renderer->d.vram)[charBase];
854 if (!mapData.hflip) {
855 tileData >>= x * 4;
856 } else {
857 tileData >>= (7 - x) * 4;
858 }
859 tileData &= 0xF;
860 tileData |= tileData << 4;
861 tileData |= tileData << 8;
862 tileData |= tileData << 12;
863 tileData |= tileData << 16;
864 tileData |= tileData << 20;
865 tileData |= tileData << 24;
866 tileData |= tileData << 28;
867 carryData = tileData;
868 mosaicWait = mosaicH;
869 }
870 --mosaicWait;
871 BACKGROUND_DRAW_PIXEL_16;
872 ++outX;
873 }
874 }
875 } else {
876 // TODO: 256-color mosaic
877 }
878 return;
879 }
880
881 if (!background->multipalette) {
882 for (; tileX < tileEnd; ++tileX) {
883 BACKGROUND_TEXT_SELECT_CHARACTER;
884 paletteData = mapData.palette << 4;
885 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
886 tileData = ((uint32_t*)renderer->d.vram)[charBase];
887 if (tileData) {
888 if (!mapData.hflip) {
889 BACKGROUND_DRAW_PIXEL_16;
890 ++outX;
891 BACKGROUND_DRAW_PIXEL_16;
892 ++outX;
893 BACKGROUND_DRAW_PIXEL_16;
894 ++outX;
895 BACKGROUND_DRAW_PIXEL_16;
896 ++outX;
897 BACKGROUND_DRAW_PIXEL_16;
898 ++outX;
899 BACKGROUND_DRAW_PIXEL_16;
900 ++outX;
901 BACKGROUND_DRAW_PIXEL_16;
902 ++outX;
903 BACKGROUND_DRAW_PIXEL_16;
904 ++outX;
905 } else {
906 outX += 7;
907 BACKGROUND_DRAW_PIXEL_16;
908 --outX;
909 BACKGROUND_DRAW_PIXEL_16;
910 --outX;
911 BACKGROUND_DRAW_PIXEL_16;
912 --outX;
913 BACKGROUND_DRAW_PIXEL_16;
914 --outX;
915 BACKGROUND_DRAW_PIXEL_16;
916 --outX;
917 BACKGROUND_DRAW_PIXEL_16;
918 --outX;
919 BACKGROUND_DRAW_PIXEL_16;
920 --outX;
921 BACKGROUND_DRAW_PIXEL_16;
922 outX += 8;
923 }
924 } else {
925 outX += 8;
926 }
927 }
928 } else {
929 for (; tileX < tileEnd; ++tileX) {
930 BACKGROUND_TEXT_SELECT_CHARACTER;
931 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
932 if (!mapData.hflip) {
933 tileData = ((uint32_t*)renderer->d.vram)[charBase];
934 if (tileData) {
935 BACKGROUND_DRAW_PIXEL_256;
936 ++outX;
937 BACKGROUND_DRAW_PIXEL_256;
938 ++outX;
939 BACKGROUND_DRAW_PIXEL_256;
940 ++outX;
941 BACKGROUND_DRAW_PIXEL_256;
942 ++outX;
943 } else {
944 outX += 4;
945 }
946 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
947 if (tileData) {
948 BACKGROUND_DRAW_PIXEL_256;
949 ++outX;
950 BACKGROUND_DRAW_PIXEL_256;
951 ++outX;
952 BACKGROUND_DRAW_PIXEL_256;
953 ++outX;
954 BACKGROUND_DRAW_PIXEL_256;
955 ++outX;
956 } else {
957 outX += 4;
958 }
959 } else {
960 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
961 if (tileData) {
962 outX += 3;
963 BACKGROUND_DRAW_PIXEL_256;
964 --outX;
965 BACKGROUND_DRAW_PIXEL_256;
966 --outX;
967 BACKGROUND_DRAW_PIXEL_256;
968 --outX;
969 BACKGROUND_DRAW_PIXEL_256;
970 outX += 4;
971 } else {
972 outX += 4;
973 }
974 tileData = ((uint32_t*)renderer->d.vram)[charBase];
975 if (tileData) {
976 outX += 3;
977 BACKGROUND_DRAW_PIXEL_256;
978 --outX;
979 BACKGROUND_DRAW_PIXEL_256;
980 --outX;
981 BACKGROUND_DRAW_PIXEL_256;
982 --outX;
983 BACKGROUND_DRAW_PIXEL_256;
984 outX += 4;
985 } else {
986 outX += 4;
987 }
988 }
989 }
990 }
991}
992
993#define BACKGROUND_BITMAP_INIT \
994 (void)(unused); \
995 int32_t x = background->sx - background->dx; \
996 int32_t y = background->sy - background->dy; \
997 int32_t localX; \
998 int32_t localY; \
999 \
1000 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
1001 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1002 flags |= FLAG_TARGET_2 * background->target2; \
1003 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1004 color_t* palette = renderer->normalPalette; \
1005 if (variant) { \
1006 palette = renderer->variantPalette; \
1007 } \
1008 PREPARE_OBJWIN;
1009
1010#define BACKGROUND_BITMAP_ITERATE(W, H) \
1011 x += background->dx; \
1012 y += background->dy; \
1013 \
1014 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1015 continue; \
1016 } else { \
1017 localX = x; \
1018 localY = y; \
1019 }
1020
1021static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1022 int sizeAdjusted = 0x8000 << background->size;
1023
1024 BACKGROUND_BITMAP_INIT;
1025
1026 uint32_t screenBase = background->screenBase;
1027 uint32_t charBase = background->charBase;
1028 uint8_t mapData;
1029 uint8_t tileData;
1030
1031 int outX;
1032 for (outX = renderer->start; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1033 x += background->dx;
1034 y += background->dy;
1035
1036 if (background->overflow) {
1037 localX = x & (sizeAdjusted - 1);
1038 localY = y & (sizeAdjusted - 1);
1039 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
1040 continue;
1041 } else {
1042 localX = x;
1043 localY = y;
1044 }
1045 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1046 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1047
1048 uint32_t current = renderer->row[outX];
1049 if (tileData && !(current & FLAG_FINALIZED)) {
1050 if (!objwinSlowPath) {
1051 _composite(renderer, outX, palette[tileData] | flags, current);
1052 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1053 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1054 _composite(renderer, outX, currentPalette[tileData] | flags, current);
1055 }
1056 }
1057 }
1058}
1059
1060static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1061 BACKGROUND_BITMAP_INIT;
1062
1063 uint16_t color;
1064 uint32_t color32;
1065
1066 int outX;
1067 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1068 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1069
1070 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1071 color32 = 0;
1072 color32 |= (color << 3) & 0xF8;
1073 color32 |= (color << 6) & 0xF800;
1074 color32 |= (color << 9) & 0xF80000;
1075
1076 uint32_t current = renderer->row[outX];
1077 if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1078 if (!variant) {
1079 _composite(renderer, outX, color32 | flags, current);
1080 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1081 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags, current);
1082 } else if (renderer->blendEffect == BLEND_DARKEN) {
1083 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags, current);
1084 }
1085 }
1086 }
1087}
1088
1089static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1090 BACKGROUND_BITMAP_INIT;
1091
1092 uint16_t color;
1093 uint32_t offset = 0;
1094 if (renderer->dispcnt.frameSelect) {
1095 offset = 0xA000;
1096 }
1097
1098 int outX;
1099 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1100 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1101
1102 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1103
1104 uint32_t current = renderer->row[outX];
1105 if (color && !(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1106 if (!variant) {
1107 _composite(renderer, outX, renderer->normalPalette[color] | flags, current);
1108 } else {
1109 _composite(renderer, outX, renderer->variantPalette[color] | flags, current);
1110 }
1111 }
1112 }
1113}
1114
1115static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1116 BACKGROUND_BITMAP_INIT;
1117
1118 uint32_t color;
1119 uint32_t offset = 0;
1120 if (renderer->dispcnt.frameSelect) {
1121 offset = 0xA000;
1122 }
1123
1124 int outX;
1125 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1126 BACKGROUND_BITMAP_ITERATE(160, 128);
1127
1128 color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1129#ifndef COLOR_16_BIT
1130 color_t color32 = 0;
1131 color32 |= (color << 9) & 0xF80000;
1132 color32 |= (color << 3) & 0xF8;
1133 color32 |= (color << 6) & 0xF800;
1134 color = color32;
1135#endif
1136
1137 uint32_t current = renderer->row[outX];
1138 if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1139 if (!variant) {
1140 _composite(renderer, outX, color | flags, current);
1141 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1142 _composite(renderer, outX, _brighten(color, renderer->bldy) | flags, current);
1143 } else if (renderer->blendEffect == BLEND_DARKEN) {
1144 _composite(renderer, outX, _darken(color, renderer->bldy) | flags, current);
1145 }
1146 }
1147 }
1148}
1149
1150static const int _objSizes[32] = {
1151 8, 8,
1152 16, 16,
1153 32, 32,
1154 64, 64,
1155 16, 8,
1156 32, 8,
1157 32, 16,
1158 64, 32,
1159 8, 16,
1160 8, 32,
1161 16, 32,
1162 32, 64,
1163 0, 0,
1164 0, 0,
1165 0, 0,
1166 0, 0
1167};
1168
1169#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1170 SPRITE_YBASE_ ## DEPTH(inY); \
1171 int outX = x >= start ? x : start; \
1172 int condition = x + width; \
1173 if (end < condition) { \
1174 condition = end; \
1175 } \
1176 for (; outX < condition; ++outX) { \
1177 int inX = outX - x; \
1178 if (sprite->hflip) { \
1179 inX = width - inX - 1; \
1180 } \
1181 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1182 continue; \
1183 } \
1184 SPRITE_XBASE_ ## DEPTH(inX); \
1185 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1186 }
1187
1188#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1189 int outX; \
1190 for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1191 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1192 continue; \
1193 } \
1194 int inX = outX - x; \
1195 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1196 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1197 \
1198 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1199 continue; \
1200 } \
1201 \
1202 SPRITE_YBASE_ ## DEPTH(localY); \
1203 SPRITE_XBASE_ ## DEPTH(localX); \
1204 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1205 }
1206
1207#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1208#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1209
1210#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1211 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1212 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1213 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1214 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1215 }
1216
1217#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1218 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1219 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1220 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1221 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1222 }
1223
1224#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1225 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1226 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1227 if (tileData) { \
1228 renderer->row[outX] |= FLAG_OBJWIN; \
1229 }
1230
1231#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1232#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1233
1234#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1235 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1236 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1237 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1238 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1239 }
1240
1241#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1242 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1243 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1244 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1245 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1246 }
1247
1248#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1249 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1250 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1251 if (tileData) { \
1252 renderer->row[outX] |= FLAG_OBJWIN; \
1253 }
1254
1255static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1256 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1257 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1258 int start = renderer->start;
1259 int end = renderer->end;
1260 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1261 return;
1262 }
1263 uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1264 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1265 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1266 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1267 int x = sprite->x;
1268 int inY = y - sprite->y;
1269 if (sprite->y + height - 256 >= 0) {
1270 inY += 256;
1271 }
1272 if (sprite->vflip) {
1273 inY = height - inY - 1;
1274 }
1275 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1276 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1277 if (!sprite->multipalette) {
1278 if (flags & FLAG_OBJWIN) {
1279 SPRITE_NORMAL_LOOP(16, OBJWIN);
1280 } else if (!variant) {
1281 SPRITE_NORMAL_LOOP(16, NORMAL);
1282 } else {
1283 SPRITE_NORMAL_LOOP(16, VARIANT);
1284 }
1285 } else {
1286 if (flags & FLAG_OBJWIN) {
1287 SPRITE_NORMAL_LOOP(256, OBJWIN);
1288 } else if (!variant) {
1289 SPRITE_NORMAL_LOOP(256, NORMAL);
1290 } else {
1291 SPRITE_NORMAL_LOOP(256, VARIANT);
1292 }
1293 }
1294}
1295
1296static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1297 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1298 int totalWidth = width << sprite->doublesize;
1299 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1300 int totalHeight = height << sprite->doublesize;
1301 int start = renderer->start;
1302 int end = renderer->end;
1303 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1304 return;
1305 }
1306 uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1307 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1308 flags |= FLAG_TARGET_2 * renderer->target2Obj;
1309 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1310 int x = sprite->x;
1311 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1312 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1313 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1314 int inY = y - sprite->y;
1315 if (inY < 0) {
1316 inY += 256;
1317 }
1318 if (!sprite->multipalette) {
1319 if (flags & FLAG_OBJWIN) {
1320 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1321 } else if (!variant) {
1322 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1323 } else {
1324 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1325 }
1326 } else {
1327 if (flags & FLAG_OBJWIN) {
1328 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1329 } else if (!variant) {
1330 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1331 } else {
1332 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1333 }
1334 }
1335}
1336
1337static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1338 int x;
1339 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
1340 uint32_t color = renderer->spriteLayer[x];
1341 uint32_t current = renderer->row[x];
1342 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1343 _composite(renderer, x, color & ~FLAG_FINALIZED, current);
1344 }
1345 }
1346}
1347
1348static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1349 int i;
1350 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1351 for (i = 0; i < 512; ++i) {
1352 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1353 }
1354 } else if (renderer->blendEffect == BLEND_DARKEN) {
1355 for (i = 0; i < 512; ++i) {
1356 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1357 }
1358 } else {
1359 for (i = 0; i < 512; ++i) {
1360 renderer->variantPalette[i] = renderer->normalPalette[i];
1361 }
1362 }
1363}
1364
1365static inline color_t _brighten(color_t color, int y) {
1366 color_t c = 0;
1367 color_t a;
1368#ifdef COLOR_16_BIT
1369 a = color & 0x1F;
1370 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1371
1372 a = color & 0x3E0;
1373 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1374
1375 a = color & 0x7C00;
1376 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1377#else
1378 a = color & 0xF8;
1379 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1380
1381 a = color & 0xF800;
1382 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1383
1384 a = color & 0xF80000;
1385 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1386#endif
1387 return c;
1388}
1389
1390static inline color_t _darken(color_t color, int y) {
1391 color_t c = 0;
1392 color_t a;
1393#ifdef COLOR_16_BIT
1394 a = color & 0x1F;
1395 c |= (a - (a * y) / 16) & 0x1F;
1396
1397 a = color & 0x3E0;
1398 c |= (a - (a * y) / 16) & 0x3E0;
1399
1400 a = color & 0x7C00;
1401 c |= (a - (a * y) / 16) & 0x7C00;
1402#else
1403 a = color & 0xF8;
1404 c |= (a - (a * y) / 16) & 0xF8;
1405
1406 a = color & 0xF800;
1407 c |= (a - (a * y) / 16) & 0xF800;
1408
1409 a = color & 0xF80000;
1410 c |= (a - (a * y) / 16) & 0xF80000;
1411#endif
1412 return c;
1413}
1414
1415static color_t _mix(int weightA, color_t colorA, int weightB, color_t colorB) {
1416 color_t c = 0;
1417 color_t a, b;
1418#ifdef COLOR_16_BIT
1419 a = colorA & 0x1F;
1420 b = colorB & 0x1F;
1421 c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1422 if (c & 0x0020) {
1423 c = 0x001F;
1424 }
1425
1426 a = colorA & 0x3E0;
1427 b = colorB & 0x3E0;
1428 c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1429 if (c & 0x0400) {
1430 c |= 0x03E0;
1431 }
1432
1433 a = colorA & 0x7C00;
1434 b = colorB & 0x7C00;
1435 c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1436 if (c & 0x8000) {
1437 c |= 0x7C00;
1438 }
1439#else
1440 a = colorA & 0xF8;
1441 b = colorB & 0xF8;
1442 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1443 if (c & 0x00000100) {
1444 c = 0x000000F8;
1445 }
1446
1447 a = colorA & 0xF800;
1448 b = colorB & 0xF800;
1449 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1450 if (c & 0x00010000) {
1451 c = (c & 0x000000F8) | 0x0000F800;
1452 }
1453
1454 a = colorA & 0xF80000;
1455 b = colorB & 0xF80000;
1456 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1457 if (c & 0x01000000) {
1458 c = (c & 0x0000F8F8) | 0x00F80000;
1459 }
1460#endif
1461 return c;
1462}