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 int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
35static int _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 renderer->anyTarget2 = bldcnt.packed & 0x3F00;
521
522 if (oldEffect != renderer->blendEffect) {
523 _updatePalettes(renderer);
524 }
525}
526
527#define TEST_LAYER_ENABLED(X) \
528 (renderer->bg[X].enabled && \
529 (renderer->currentWindow.bg ## X ## Enable || \
530 (renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
531 renderer->bg[X].priority == priority)
532
533static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
534 int w;
535 renderer->end = 0;
536 int spriteLayers = 0;
537 if (renderer->dispcnt.objEnable) {
538 for (w = 0; w < renderer->nWindows; ++w) {
539 renderer->start = renderer->end;
540 renderer->end = renderer->windows[w].endX;
541 renderer->currentWindow = renderer->windows[w].control;
542 if (!renderer->currentWindow.objEnable) {
543 continue;
544 }
545 int i;
546 int drawn;
547 for (i = 0; i < 128; ++i) {
548 struct GBAObj* sprite = &renderer->d.oam->obj[i];
549 if (sprite->transformed) {
550 drawn = _preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
551 spriteLayers |= drawn << sprite->priority;
552 } else if (!sprite->disable) {
553 drawn = _preprocessSprite(renderer, sprite, y);
554 spriteLayers |= drawn << sprite->priority;
555 }
556 }
557 }
558 }
559
560 int priority;
561 for (priority = 0; priority < 4; ++priority) {
562 if (spriteLayers & (1 << priority)) {
563 _postprocessSprite(renderer, priority);
564 }
565 renderer->end = 0;
566 for (w = 0; w < renderer->nWindows; ++w) {
567 renderer->start = renderer->end;
568 renderer->end = renderer->windows[w].endX;
569 renderer->currentWindow = renderer->windows[w].control;
570 if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
571 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
572 }
573 if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
574 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
575 }
576 if (TEST_LAYER_ENABLED(2)) {
577 switch (renderer->dispcnt.mode) {
578 case 0:
579 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
580 break;
581 case 1:
582 case 2:
583 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
584 break;
585 case 3:
586 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
587 break;
588 case 4:
589 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
590 break;
591 case 5:
592 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
593 break;
594 }
595 }
596 if (TEST_LAYER_ENABLED(3)) {
597 switch (renderer->dispcnt.mode) {
598 case 0:
599 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
600 break;
601 case 2:
602 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
603 break;
604 }
605 }
606 }
607 }
608 renderer->bg[2].sx += renderer->bg[2].dmx;
609 renderer->bg[2].sy += renderer->bg[2].dmy;
610 renderer->bg[3].sx += renderer->bg[3].dmx;
611 renderer->bg[3].sy += renderer->bg[3].dmy;
612}
613
614static void _composite(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
615 // We stash the priority on the top bits so we can do a one-operator comparison
616 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
617 // We want to do special processing if the color pixel is target 1, however
618 if (current & FLAG_UNWRITTEN) {
619 *pixel = color | (current & FLAG_OBJWIN);
620 } else if ((color & FLAG_ORDER_MASK) < (current & FLAG_ORDER_MASK)) {
621 if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
622 *pixel = color | FLAG_FINALIZED;
623 } else {
624 *pixel = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
625 }
626 } else {
627 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
628 *pixel = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
629 } else {
630 *pixel = current | FLAG_FINALIZED;
631 }
632 }
633}
634
635#define BACKGROUND_DRAW_PIXEL_16 \
636 pixelData = tileData & 0xF; \
637 current = *pixel; \
638 if (pixelData && !(current & FLAG_FINALIZED)) { \
639 if (!objwinSlowPath) { \
640 _composite(renderer, pixel, palette[pixelData | paletteData] | flags, current); \
641 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
642 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette; \
643 _composite(renderer, pixel, currentPalette[pixelData | paletteData] | flags, current); \
644 } \
645 } \
646 tileData >>= 4;
647
648#define BACKGROUND_DRAW_PIXEL_256 \
649 pixelData = tileData & 0xFF; \
650 current = *pixel; \
651 if (pixelData && !(current & FLAG_FINALIZED)) { \
652 if (!objwinSlowPath) { \
653 _composite(renderer, pixel, palette[pixelData] | flags, current); \
654 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
655 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette; \
656 _composite(renderer, pixel, currentPalette[pixelData] | flags, current); \
657 } \
658 } \
659 tileData >>= 8;
660
661#define BACKGROUND_TEXT_SELECT_CHARACTER \
662 localX = tileX * 8 + inX; \
663 xBase = localX & 0xF8; \
664 if (background->size & 1) { \
665 xBase += (localX & 0x100) << 5; \
666 } \
667 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
668 mapData = renderer->d.vram[screenBase]; \
669 if (!GBA_TEXT_MAP_VFLIP(mapData)) { \
670 localY = inY & 0x7; \
671 } else { \
672 localY = 7 - (inY & 0x7); \
673 }
674
675#define PREPARE_OBJWIN \
676 int objwinSlowPath = renderer->dispcnt.objwinEnable; \
677 int objwinOnly = 0; \
678 int objwinForceEnable = 0; \
679 color_t* objwinPalette; \
680 if (objwinSlowPath) { \
681 if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
682 objwinPalette = renderer->variantPalette; \
683 } else { \
684 objwinPalette = renderer->normalPalette; \
685 } \
686 switch (background->index) { \
687 case 0: \
688 objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \
689 objwinOnly = !renderer->objwin.bg0Enable; \
690 break; \
691 case 1: \
692 objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \
693 objwinOnly = !renderer->objwin.bg1Enable; \
694 break; \
695 case 2: \
696 objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \
697 objwinOnly = !renderer->objwin.bg2Enable; \
698 break; \
699 case 3: \
700 objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \
701 objwinOnly = !renderer->objwin.bg3Enable; \
702 break; \
703 } \
704 }
705
706static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
707 int inX = renderer->start + background->x;
708 if (background->mosaic) {
709 int mosaicV = renderer->mosaic.bgV + 1;
710 y -= y % mosaicV;
711 }
712 int inY = y + background->y;
713 uint16_t mapData;
714
715 unsigned yBase = inY & 0xF8;
716 if (background->size == 2) {
717 yBase += inY & 0x100;
718 } else if (background->size == 3) {
719 yBase += (inY & 0x100) << 1;
720 }
721
722 int localX;
723 int localY;
724
725 unsigned xBase;
726
727 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
728 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
729 flags |= FLAG_TARGET_2 * background->target2;
730 if (!renderer->anyTarget2) {
731 flags |= FLAG_FINALIZED;
732 }
733
734 uint32_t screenBase;
735 uint32_t charBase;
736 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
737 color_t* palette = renderer->normalPalette;
738 if (variant) {
739 palette = renderer->variantPalette;
740 }
741 PREPARE_OBJWIN;
742
743 int outX = renderer->start;
744
745 uint32_t tileData;
746 uint32_t current;
747 int pixelData;
748 int paletteData;
749 int tileX = 0;
750 int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
751
752 if (inX & 0x7) {
753 int mod8 = inX & 0x7;
754 BACKGROUND_TEXT_SELECT_CHARACTER;
755
756 int end = outX + 0x8 - mod8;
757 if (!background->multipalette) {
758 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
759 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
760 tileData = ((uint32_t*)renderer->d.vram)[charBase];
761 if (!GBA_TEXT_MAP_HFLIP(mapData)) {
762 tileData >>= 4 * mod8;
763 for (; outX < end; ++outX) {
764 uint32_t* pixel = &renderer->row[outX];
765 BACKGROUND_DRAW_PIXEL_16;
766 }
767 } else {
768 for (outX = end - 1; outX >= renderer->start; --outX) {
769 uint32_t* pixel = &renderer->row[outX];
770 BACKGROUND_DRAW_PIXEL_16;
771 }
772 }
773 } else {
774 // TODO: hflip
775 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
776 int end2 = end - 4;
777 int shift = inX & 0x3;
778 if (end2 > 0) {
779 tileData = ((uint32_t*)renderer->d.vram)[charBase];
780 tileData >>= 8 * shift;
781 shift = 0;
782 for (; outX < end2; ++outX) {
783 uint32_t* pixel = &renderer->row[outX];
784 BACKGROUND_DRAW_PIXEL_256;
785 }
786 }
787
788 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
789 tileData >>= 8 * shift;
790 for (; outX < end; ++outX) {
791 uint32_t* pixel = &renderer->row[outX];
792 BACKGROUND_DRAW_PIXEL_256;
793 }
794 }
795 }
796 if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) {
797 tileX = tileEnd;
798 int pixelData, paletteData;
799 int mod8 = (inX + renderer->end - renderer->start) & 0x7;
800 BACKGROUND_TEXT_SELECT_CHARACTER;
801
802 int end = 0x8 - mod8;
803 if (!background->multipalette) {
804 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
805 tileData = ((uint32_t*)renderer->d.vram)[charBase];
806 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
807 if (!GBA_TEXT_MAP_HFLIP(mapData)) {
808 outX = renderer->end - mod8;
809 if (outX < 0) {
810 tileData >>= 4 * -outX;
811 outX = 0;
812 }
813 for (; outX < renderer->end; ++outX) {
814 uint32_t* pixel = &renderer->row[outX];
815 BACKGROUND_DRAW_PIXEL_16;
816 }
817 } else {
818 tileData >>= 4 * (0x8 - mod8);
819 int end2 = renderer->end - 8;
820 if (end2 < -1) {
821 end2 = -1;
822 }
823 for (outX = renderer->end - 1; outX > end2; --outX) {
824 uint32_t* pixel = &renderer->row[outX];
825 BACKGROUND_DRAW_PIXEL_16;
826 }
827 }
828 } else {
829 // TODO: hflip
830 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
831 outX = renderer->end - 8 + end;
832 int end2 = 4 - end;
833 if (end2 > 0) {
834 tileData = ((uint32_t*)renderer->d.vram)[charBase];
835 for (; outX < renderer->end - end2; ++outX) {
836 uint32_t* pixel = &renderer->row[outX];
837 BACKGROUND_DRAW_PIXEL_256;
838 }
839 ++charBase;
840 }
841
842 tileData = ((uint32_t*)renderer->d.vram)[charBase];
843 for (; outX < renderer->end; ++outX) {
844 uint32_t* pixel = &renderer->row[outX];
845 BACKGROUND_DRAW_PIXEL_256;
846 }
847 }
848
849 tileX = (inX & 0x7) != 0;
850 outX = renderer->start + tileX * 8 - (inX & 0x7);
851 }
852
853 uint32_t* pixel = &renderer->row[outX];
854 if (background->mosaic) {
855 int mosaicH = renderer->mosaic.bgH + 1;
856 int x;
857 int mosaicWait = outX % mosaicH;
858 int carryData = 0;
859
860 if (!background->multipalette) {
861 for (; tileX < tileEnd; ++tileX) {
862 BACKGROUND_TEXT_SELECT_CHARACTER;
863 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
864 tileData = carryData;
865 for (x = 0; x < 8; ++x) {
866 if (!mosaicWait) {
867 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
868 tileData = ((uint32_t*)renderer->d.vram)[charBase];
869 if (!GBA_TEXT_MAP_HFLIP(mapData)) {
870 tileData >>= x * 4;
871 } else {
872 tileData >>= (7 - x) * 4;
873 }
874 tileData &= 0xF;
875 tileData |= tileData << 4;
876 tileData |= tileData << 8;
877 tileData |= tileData << 12;
878 tileData |= tileData << 16;
879 tileData |= tileData << 20;
880 tileData |= tileData << 24;
881 tileData |= tileData << 28;
882 carryData = tileData;
883 mosaicWait = mosaicH;
884 }
885 --mosaicWait;
886 BACKGROUND_DRAW_PIXEL_16;
887 ++pixel;
888 }
889 }
890 } else {
891 // TODO: 256-color mosaic
892 }
893 return;
894 }
895
896 if (!background->multipalette) {
897 for (; tileX < tileEnd; ++tileX) {
898 BACKGROUND_TEXT_SELECT_CHARACTER;
899 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
900 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
901 tileData = ((uint32_t*)renderer->d.vram)[charBase];
902 if (tileData) {
903 if (!GBA_TEXT_MAP_HFLIP(mapData)) {
904 BACKGROUND_DRAW_PIXEL_16;
905 ++pixel;
906 BACKGROUND_DRAW_PIXEL_16;
907 ++pixel;
908 BACKGROUND_DRAW_PIXEL_16;
909 ++pixel;
910 BACKGROUND_DRAW_PIXEL_16;
911 ++pixel;
912 BACKGROUND_DRAW_PIXEL_16;
913 ++pixel;
914 BACKGROUND_DRAW_PIXEL_16;
915 ++pixel;
916 BACKGROUND_DRAW_PIXEL_16;
917 ++pixel;
918 BACKGROUND_DRAW_PIXEL_16;
919 ++pixel;
920 } else {
921 pixel += 7;
922 BACKGROUND_DRAW_PIXEL_16;
923 --pixel;
924 BACKGROUND_DRAW_PIXEL_16;
925 --pixel;
926 BACKGROUND_DRAW_PIXEL_16;
927 --pixel;
928 BACKGROUND_DRAW_PIXEL_16;
929 --pixel;
930 BACKGROUND_DRAW_PIXEL_16;
931 --pixel;
932 BACKGROUND_DRAW_PIXEL_16;
933 --pixel;
934 BACKGROUND_DRAW_PIXEL_16;
935 --pixel;
936 BACKGROUND_DRAW_PIXEL_16;
937 pixel += 8;
938 }
939 } else {
940 pixel += 8;
941 }
942 }
943 } else {
944 for (; tileX < tileEnd; ++tileX) {
945 BACKGROUND_TEXT_SELECT_CHARACTER;
946 charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
947 if (!GBA_TEXT_MAP_HFLIP(mapData)) {
948 tileData = ((uint32_t*)renderer->d.vram)[charBase];
949 if (tileData) {
950 BACKGROUND_DRAW_PIXEL_256;
951 ++pixel;
952 BACKGROUND_DRAW_PIXEL_256;
953 ++pixel;
954 BACKGROUND_DRAW_PIXEL_256;
955 ++pixel;
956 BACKGROUND_DRAW_PIXEL_256;
957 ++pixel;
958 } else {
959 pixel += 4;
960 }
961 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
962 if (tileData) {
963 BACKGROUND_DRAW_PIXEL_256;
964 ++pixel;
965 BACKGROUND_DRAW_PIXEL_256;
966 ++pixel;
967 BACKGROUND_DRAW_PIXEL_256;
968 ++pixel;
969 BACKGROUND_DRAW_PIXEL_256;
970 ++pixel;
971 } else {
972 pixel += 4;
973 }
974 } else {
975 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
976 if (tileData) {
977 pixel += 3;
978 BACKGROUND_DRAW_PIXEL_256;
979 --pixel;
980 BACKGROUND_DRAW_PIXEL_256;
981 --pixel;
982 BACKGROUND_DRAW_PIXEL_256;
983 --pixel;
984 BACKGROUND_DRAW_PIXEL_256;
985 }
986 pixel += 4;
987 tileData = ((uint32_t*)renderer->d.vram)[charBase];
988 if (tileData) {
989 pixel += 3;
990 BACKGROUND_DRAW_PIXEL_256;
991 --pixel;
992 BACKGROUND_DRAW_PIXEL_256;
993 --pixel;
994 BACKGROUND_DRAW_PIXEL_256;
995 --pixel;
996 BACKGROUND_DRAW_PIXEL_256;
997 }
998 pixel += 4;
999 }
1000 }
1001 }
1002}
1003
1004#define BACKGROUND_BITMAP_INIT \
1005 (void)(unused); \
1006 int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1007 int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1008 int32_t localX; \
1009 int32_t localY; \
1010 \
1011 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
1012 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1013 flags |= FLAG_TARGET_2 * background->target2; \
1014 if (!renderer->anyTarget2) { \
1015 flags |= FLAG_FINALIZED; \
1016 } \
1017 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1018 color_t* palette = renderer->normalPalette; \
1019 if (variant) { \
1020 palette = renderer->variantPalette; \
1021 } \
1022 PREPARE_OBJWIN;
1023
1024#define BACKGROUND_BITMAP_ITERATE(W, H) \
1025 x += background->dx; \
1026 y += background->dy; \
1027 \
1028 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1029 continue; \
1030 } else { \
1031 localX = x; \
1032 localY = y; \
1033 }
1034
1035static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1036 int sizeAdjusted = 0x8000 << background->size;
1037
1038 BACKGROUND_BITMAP_INIT;
1039
1040 uint32_t screenBase = background->screenBase;
1041 uint32_t charBase = background->charBase;
1042 uint8_t mapData;
1043 uint8_t tileData;
1044
1045 int outX;
1046 uint32_t* pixel;
1047 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1048 x += background->dx;
1049 y += background->dy;
1050
1051 if (background->overflow) {
1052 localX = x & (sizeAdjusted - 1);
1053 localY = y & (sizeAdjusted - 1);
1054 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
1055 continue;
1056 } else {
1057 localX = x;
1058 localY = y;
1059 }
1060 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1061 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1062
1063 uint32_t current = *pixel;
1064 if (tileData && !(current & FLAG_FINALIZED)) {
1065 if (!objwinSlowPath) {
1066 _composite(renderer, pixel, palette[tileData] | flags, current);
1067 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1068 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1069 _composite(renderer, pixel, currentPalette[tileData] | flags, current);
1070 }
1071 }
1072 }
1073}
1074
1075static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1076 BACKGROUND_BITMAP_INIT;
1077
1078 uint32_t color;
1079
1080 int outX;
1081 uint32_t* pixel;
1082 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1083 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1084
1085 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1086#ifndef COLOR_16_BIT
1087 color_t color32;
1088 color32 = 0;
1089 color32 |= (color << 3) & 0xF8;
1090 color32 |= (color << 6) & 0xF800;
1091 color32 |= (color << 9) & 0xF80000;
1092 color = color32;
1093#endif
1094
1095 uint32_t current = *pixel;
1096 if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1097 if (!variant) {
1098 _composite(renderer, pixel, color | flags, current);
1099 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1100 _composite(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1101 } else if (renderer->blendEffect == BLEND_DARKEN) {
1102 _composite(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1103 }
1104 }
1105 }
1106}
1107
1108static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1109 BACKGROUND_BITMAP_INIT;
1110
1111 uint16_t color;
1112 uint32_t offset = 0;
1113 if (renderer->dispcnt.frameSelect) {
1114 offset = 0xA000;
1115 }
1116
1117 int outX;
1118 uint32_t* pixel;
1119 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1120 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1121
1122 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1123
1124 uint32_t current = *pixel;
1125 if (color && !(current & FLAG_FINALIZED)) {
1126 if (!objwinSlowPath) {
1127 _composite(renderer, pixel, palette[color] | flags, current);
1128 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1129 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1130 _composite(renderer, pixel, currentPalette[color] | flags, current);
1131 }
1132 }
1133 }
1134}
1135
1136static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1137 BACKGROUND_BITMAP_INIT;
1138
1139 uint32_t color;
1140 uint32_t offset = 0;
1141 if (renderer->dispcnt.frameSelect) {
1142 offset = 0xA000;
1143 }
1144
1145 int outX;
1146 uint32_t* pixel;
1147 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1148 BACKGROUND_BITMAP_ITERATE(160, 128);
1149
1150 color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1151#ifndef COLOR_16_BIT
1152 color_t color32 = 0;
1153 color32 |= (color << 9) & 0xF80000;
1154 color32 |= (color << 3) & 0xF8;
1155 color32 |= (color << 6) & 0xF800;
1156 color = color32;
1157#endif
1158
1159 uint32_t current = *pixel;
1160 if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1161 if (!variant) {
1162 _composite(renderer, pixel, color | flags, current);
1163 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1164 _composite(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1165 } else if (renderer->blendEffect == BLEND_DARKEN) {
1166 _composite(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1167 }
1168 }
1169 }
1170}
1171
1172static const int _objSizes[32] = {
1173 8, 8,
1174 16, 16,
1175 32, 32,
1176 64, 64,
1177 16, 8,
1178 32, 8,
1179 32, 16,
1180 64, 32,
1181 8, 16,
1182 8, 32,
1183 16, 32,
1184 32, 64,
1185 0, 0,
1186 0, 0,
1187 0, 0,
1188 0, 0
1189};
1190
1191#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1192 SPRITE_YBASE_ ## DEPTH(inY); \
1193 for (; outX < condition; ++outX, inX += xOffset) { \
1194 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1195 continue; \
1196 } \
1197 SPRITE_XBASE_ ## DEPTH(inX); \
1198 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1199 }
1200
1201#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1202 SPRITE_YBASE_ ## DEPTH(inY); \
1203 if (outX % mosaicH) { \
1204 inX += (mosaicH - (outX % mosaicH)) * xOffset; \
1205 outX += mosaicH - (outX % mosaicH); \
1206 } \
1207 for (; outX < condition; ++outX, inX += xOffset) { \
1208 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1209 continue; \
1210 } \
1211 int localX = inX - xOffset * (outX % mosaicH); \
1212 SPRITE_XBASE_ ## DEPTH(localX); \
1213 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1214 }
1215
1216#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1217 int outX; \
1218 for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1219 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1220 continue; \
1221 } \
1222 int inX = outX - x; \
1223 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1224 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1225 \
1226 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1227 continue; \
1228 } \
1229 \
1230 SPRITE_YBASE_ ## DEPTH(localY); \
1231 SPRITE_XBASE_ ## DEPTH(localX); \
1232 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1233 }
1234
1235#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1236#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1237
1238#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1239 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1240 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1241 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1242 renderer->spriteLayer[outX] = palette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1243 }
1244
1245#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1246 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1247 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1248 if (tileData) { \
1249 renderer->row[outX] |= FLAG_OBJWIN; \
1250 }
1251
1252#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1253#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1254
1255#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1256 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1257 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1258 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1259 renderer->spriteLayer[outX] = palette[0x100 | tileData] | flags; \
1260 }
1261
1262#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1263 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1264 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1265 if (tileData) { \
1266 renderer->row[outX] |= FLAG_OBJWIN; \
1267 }
1268
1269static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1270 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1271 if (sprite->mosaic) {
1272 int mosaicV = renderer->mosaic.objV + 1;
1273 y -= y % mosaicV;
1274 }
1275 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1276 return 0;
1277 }
1278 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1279 int start = renderer->start;
1280 int end = renderer->end;
1281 uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1282 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1283 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1284 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1285 int x = sprite->x;
1286 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1287 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1288 if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1289 // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1290 variant = 0;
1291 }
1292 color_t* palette = renderer->normalPalette;
1293 if (variant) {
1294 palette = renderer->variantPalette;
1295 }
1296
1297 int outX = x >= start ? x : start;
1298 int condition = x + width;
1299 int mosaicH = 1;
1300 if (sprite->mosaic) {
1301 mosaicH = renderer->mosaic.objH + 1;
1302 if (condition % mosaicH) {
1303 condition += mosaicH - (condition % mosaicH);
1304 }
1305 }
1306 int inY = y - sprite->y;
1307 if (sprite->y + height - 256 >= 0) {
1308 inY += 256;
1309 }
1310 if (sprite->vflip) {
1311 inY = height - inY - 1;
1312 }
1313 if (end < condition) {
1314 condition = end;
1315 }
1316 int inX = outX - x;
1317 int xOffset = 1;
1318 if (sprite->hflip) {
1319 inX = width - inX - 1;
1320 xOffset = -1;
1321 }
1322 if (!sprite->multipalette) {
1323 if (flags & FLAG_OBJWIN) {
1324 SPRITE_NORMAL_LOOP(16, OBJWIN);
1325 } else if (sprite->mosaic) {
1326 SPRITE_MOSAIC_LOOP(16, NORMAL);
1327 } else {
1328 SPRITE_NORMAL_LOOP(16, NORMAL);
1329 }
1330 } else {
1331 if (flags & FLAG_OBJWIN) {
1332 SPRITE_NORMAL_LOOP(256, OBJWIN);
1333 } else if (sprite->mosaic) {
1334 SPRITE_MOSAIC_LOOP(256, NORMAL);
1335 } else {
1336 SPRITE_NORMAL_LOOP(256, NORMAL);
1337 }
1338 }
1339 return 1;
1340}
1341
1342static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1343 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1344 int totalHeight = height << sprite->doublesize;
1345 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1346 return 0;
1347 }
1348 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1349 int totalWidth = width << sprite->doublesize;
1350 int start = renderer->start;
1351 int end = renderer->end;
1352 uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1353 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1354 flags |= FLAG_TARGET_2 * renderer->target2Obj;
1355 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1356 int x = sprite->x;
1357 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1358 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1359 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1360 if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1361 // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1362 variant = 0;
1363 }
1364 color_t* palette = renderer->normalPalette;
1365 if (variant) {
1366 palette = renderer->variantPalette;
1367 }
1368 int inY = y - sprite->y;
1369 if (inY < 0) {
1370 inY += 256;
1371 }
1372 if (!sprite->multipalette) {
1373 if (flags & FLAG_OBJWIN) {
1374 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1375 } else {
1376 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1377 }
1378 } else {
1379 if (flags & FLAG_OBJWIN) {
1380 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1381 } else {
1382 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1383 }
1384 }
1385 return 1;
1386}
1387
1388static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1389 int x;
1390 uint32_t* pixel = renderer->row;
1391 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1392 uint32_t color = renderer->spriteLayer[x];
1393 uint32_t current = *pixel;
1394 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1395 _composite(renderer, pixel, color & ~FLAG_FINALIZED, current);
1396 }
1397 }
1398}
1399
1400static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1401 int i;
1402 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1403 for (i = 0; i < 512; ++i) {
1404 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1405 }
1406 } else if (renderer->blendEffect == BLEND_DARKEN) {
1407 for (i = 0; i < 512; ++i) {
1408 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1409 }
1410 } else {
1411 for (i = 0; i < 512; ++i) {
1412 renderer->variantPalette[i] = renderer->normalPalette[i];
1413 }
1414 }
1415}
1416
1417static inline color_t _brighten(color_t color, int y) {
1418 color_t c = 0;
1419 color_t a;
1420#ifdef COLOR_16_BIT
1421 a = color & 0x1F;
1422 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1423
1424 a = color & 0x3E0;
1425 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1426
1427 a = color & 0x7C00;
1428 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1429#else
1430 a = color & 0xF8;
1431 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1432
1433 a = color & 0xF800;
1434 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1435
1436 a = color & 0xF80000;
1437 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1438#endif
1439 return c;
1440}
1441
1442static inline color_t _darken(color_t color, int y) {
1443 color_t c = 0;
1444 color_t a;
1445#ifdef COLOR_16_BIT
1446 a = color & 0x1F;
1447 c |= (a - (a * y) / 16) & 0x1F;
1448
1449 a = color & 0x3E0;
1450 c |= (a - (a * y) / 16) & 0x3E0;
1451
1452 a = color & 0x7C00;
1453 c |= (a - (a * y) / 16) & 0x7C00;
1454#else
1455 a = color & 0xF8;
1456 c |= (a - (a * y) / 16) & 0xF8;
1457
1458 a = color & 0xF800;
1459 c |= (a - (a * y) / 16) & 0xF800;
1460
1461 a = color & 0xF80000;
1462 c |= (a - (a * y) / 16) & 0xF80000;
1463#endif
1464 return c;
1465}
1466
1467static color_t _mix(int weightA, color_t colorA, int weightB, color_t colorB) {
1468 color_t c = 0;
1469 color_t a, b;
1470#ifdef COLOR_16_BIT
1471 a = colorA & 0x1F;
1472 b = colorB & 0x1F;
1473 c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1474 if (c & 0x0020) {
1475 c = 0x001F;
1476 }
1477
1478 a = colorA & 0x3E0;
1479 b = colorB & 0x3E0;
1480 c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1481 if (c & 0x0400) {
1482 c |= 0x03E0;
1483 }
1484
1485 a = colorA & 0x7C00;
1486 b = colorB & 0x7C00;
1487 c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1488 if (c & 0x8000) {
1489 c |= 0x7C00;
1490 }
1491#else
1492 a = colorA & 0xF8;
1493 b = colorB & 0xF8;
1494 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1495 if (c & 0x00000100) {
1496 c = 0x000000F8;
1497 }
1498
1499 a = colorA & 0xF800;
1500 b = colorB & 0xF800;
1501 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1502 if (c & 0x00010000) {
1503 c = (c & 0x000000F8) | 0x0000F800;
1504 }
1505
1506 a = colorA & 0xF80000;
1507 b = colorB & 0xF80000;
1508 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1509 if (c & 0x01000000) {
1510 c = (c & 0x0000F8F8) | 0x00F80000;
1511 }
1512#endif
1513 return c;
1514}