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