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