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 int outX = x >= start ? x : start; \
1177 int condition = x + width; \
1178 if (end < condition) { \
1179 condition = end; \
1180 } \
1181 for (; outX < condition; ++outX) { \
1182 int inX = outX - x; \
1183 if (sprite->hflip) { \
1184 inX = width - inX - 1; \
1185 } \
1186 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1187 continue; \
1188 } \
1189 SPRITE_XBASE_ ## DEPTH(inX); \
1190 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1191 }
1192
1193#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1194 int outX; \
1195 for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1196 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1197 continue; \
1198 } \
1199 int inX = outX - x; \
1200 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1201 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1202 \
1203 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1204 continue; \
1205 } \
1206 \
1207 SPRITE_YBASE_ ## DEPTH(localY); \
1208 SPRITE_XBASE_ ## DEPTH(localX); \
1209 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1210 }
1211
1212#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1213#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1214
1215#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1216 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1217 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1218 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1219 renderer->spriteLayer[outX] = palette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1220 }
1221
1222#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1223 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1224 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1225 if (tileData) { \
1226 renderer->row[outX] |= FLAG_OBJWIN; \
1227 }
1228
1229#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1230#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1231
1232#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1233 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1234 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1235 if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1236 renderer->spriteLayer[outX] = palette[0x100 | tileData] | flags; \
1237 }
1238
1239#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1240 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1241 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1242 if (tileData) { \
1243 renderer->row[outX] |= FLAG_OBJWIN; \
1244 }
1245
1246static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1247 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1248 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1249 return 0;
1250 }
1251 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1252 int start = renderer->start;
1253 int end = renderer->end;
1254 uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1255 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1256 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1257 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1258 int x = sprite->x;
1259 int inY = y - sprite->y;
1260 if (sprite->y + height - 256 >= 0) {
1261 inY += 256;
1262 }
1263 if (sprite->vflip) {
1264 inY = height - inY - 1;
1265 }
1266 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1267 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1268 color_t* palette = renderer->normalPalette;
1269 if (variant) {
1270 palette = renderer->variantPalette;
1271 }
1272 if (!sprite->multipalette) {
1273 if (flags & FLAG_OBJWIN) {
1274 SPRITE_NORMAL_LOOP(16, OBJWIN);
1275 } else {
1276 SPRITE_NORMAL_LOOP(16, NORMAL);
1277 }
1278 } else {
1279 if (flags & FLAG_OBJWIN) {
1280 SPRITE_NORMAL_LOOP(256, OBJWIN);
1281 } else {
1282 SPRITE_NORMAL_LOOP(256, NORMAL);
1283 }
1284 }
1285 return 1;
1286}
1287
1288static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1289 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1290 int totalHeight = height << sprite->doublesize;
1291 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1292 return 0;
1293 }
1294 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1295 int totalWidth = width << sprite->doublesize;
1296 int start = renderer->start;
1297 int end = renderer->end;
1298 uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1299 flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1300 flags |= FLAG_TARGET_2 * renderer->target2Obj;
1301 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1302 int x = sprite->x;
1303 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1304 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1305 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1306 color_t* palette = renderer->normalPalette;
1307 if (variant) {
1308 palette = renderer->variantPalette;
1309 }
1310 int inY = y - sprite->y;
1311 if (inY < 0) {
1312 inY += 256;
1313 }
1314 if (!sprite->multipalette) {
1315 if (flags & FLAG_OBJWIN) {
1316 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1317 } else {
1318 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1319 }
1320 } else {
1321 if (flags & FLAG_OBJWIN) {
1322 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1323 } else {
1324 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1325 }
1326 }
1327 return 1;
1328}
1329
1330static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1331 int x;
1332 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
1333 uint32_t color = renderer->spriteLayer[x];
1334 uint32_t current = renderer->row[x];
1335 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1336 _composite(renderer, x, color & ~FLAG_FINALIZED, current);
1337 }
1338 }
1339}
1340
1341static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1342 int i;
1343 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1344 for (i = 0; i < 512; ++i) {
1345 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1346 }
1347 } else if (renderer->blendEffect == BLEND_DARKEN) {
1348 for (i = 0; i < 512; ++i) {
1349 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1350 }
1351 } else {
1352 for (i = 0; i < 512; ++i) {
1353 renderer->variantPalette[i] = renderer->normalPalette[i];
1354 }
1355 }
1356}
1357
1358static inline color_t _brighten(color_t color, int y) {
1359 color_t c = 0;
1360 color_t a;
1361#ifdef COLOR_16_BIT
1362 a = color & 0x1F;
1363 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1364
1365 a = color & 0x3E0;
1366 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1367
1368 a = color & 0x7C00;
1369 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1370#else
1371 a = color & 0xF8;
1372 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1373
1374 a = color & 0xF800;
1375 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1376
1377 a = color & 0xF80000;
1378 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1379#endif
1380 return c;
1381}
1382
1383static inline color_t _darken(color_t color, int y) {
1384 color_t c = 0;
1385 color_t a;
1386#ifdef COLOR_16_BIT
1387 a = color & 0x1F;
1388 c |= (a - (a * y) / 16) & 0x1F;
1389
1390 a = color & 0x3E0;
1391 c |= (a - (a * y) / 16) & 0x3E0;
1392
1393 a = color & 0x7C00;
1394 c |= (a - (a * y) / 16) & 0x7C00;
1395#else
1396 a = color & 0xF8;
1397 c |= (a - (a * y) / 16) & 0xF8;
1398
1399 a = color & 0xF800;
1400 c |= (a - (a * y) / 16) & 0xF800;
1401
1402 a = color & 0xF80000;
1403 c |= (a - (a * y) / 16) & 0xF80000;
1404#endif
1405 return c;
1406}
1407
1408static color_t _mix(int weightA, color_t colorA, int weightB, color_t colorB) {
1409 color_t c = 0;
1410 color_t a, b;
1411#ifdef COLOR_16_BIT
1412 a = colorA & 0x1F;
1413 b = colorB & 0x1F;
1414 c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1415 if (c & 0x0020) {
1416 c = 0x001F;
1417 }
1418
1419 a = colorA & 0x3E0;
1420 b = colorB & 0x3E0;
1421 c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1422 if (c & 0x0400) {
1423 c |= 0x03E0;
1424 }
1425
1426 a = colorA & 0x7C00;
1427 b = colorB & 0x7C00;
1428 c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1429 if (c & 0x8000) {
1430 c |= 0x7C00;
1431 }
1432#else
1433 a = colorA & 0xF8;
1434 b = colorB & 0xF8;
1435 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1436 if (c & 0x00000100) {
1437 c = 0x000000F8;
1438 }
1439
1440 a = colorA & 0xF800;
1441 b = colorB & 0xF800;
1442 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1443 if (c & 0x00010000) {
1444 c = (c & 0x000000F8) | 0x0000F800;
1445 }
1446
1447 a = colorA & 0xF80000;
1448 b = colorB & 0xF80000;
1449 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1450 if (c & 0x01000000) {
1451 c = (c & 0x0000F8F8) | 0x00F80000;
1452 }
1453#endif
1454 return c;
1455}