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