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