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