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