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