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