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