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