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