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