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