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