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 GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
11static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
12static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
13static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
14
15static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
16static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
17static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
18
19static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
20static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
21static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
22static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
23
24static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
25static inline uint32_t _brighten(uint32_t color, int y);
26static inline uint32_t _darken(uint32_t color, int y);
27static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB);
28
29void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
30 renderer->d.init = GBAVideoSoftwareRendererInit;
31 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
32 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
33 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
34 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
35 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
36
37 renderer->d.turbo = 0;
38 renderer->d.framesPending = 0;
39
40 {
41 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
42 renderer->mutex = mutex;
43 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
44 renderer->upCond = cond;
45 renderer->downCond = cond;
46 }
47}
48
49static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
50 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
51 int i;
52
53 softwareRenderer->dispcnt.packed = 0x0080;
54
55 softwareRenderer->target1Obj = 0;
56 softwareRenderer->target1Bd = 0;
57 softwareRenderer->target2Obj = 0;
58 softwareRenderer->target2Bd = 0;
59 softwareRenderer->blendEffect = BLEND_NONE;
60 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
61 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
62
63 softwareRenderer->blda = 0;
64 softwareRenderer->bldb = 0;
65 softwareRenderer->bldy = 0;
66
67 for (i = 0; i < 4; ++i) {
68 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
69 bg->index = i;
70 bg->enabled = 0;
71 bg->priority = 0;
72 bg->charBase = 0;
73 bg->mosaic = 0;
74 bg->multipalette = 0;
75 bg->screenBase = 0;
76 bg->overflow = 0;
77 bg->size = 0;
78 bg->target1 = 0;
79 bg->target2 = 0;
80 bg->x = 0;
81 bg->y = 0;
82 bg->refx = 0;
83 bg->refy = 0;
84 bg->dx = 256;
85 bg->dmx = 0;
86 bg->dy = 0;
87 bg->dmy = 256;
88 bg->sx = 0;
89 bg->sy = 0;
90 }
91
92 pthread_mutex_init(&softwareRenderer->mutex, 0);
93 pthread_cond_init(&softwareRenderer->upCond, 0);
94 pthread_cond_init(&softwareRenderer->downCond, 0);
95}
96
97static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
98 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
99
100 pthread_mutex_destroy(&softwareRenderer->mutex);
101 pthread_cond_destroy(&softwareRenderer->upCond);
102 pthread_cond_destroy(&softwareRenderer->downCond);
103}
104
105static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
106 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
107 switch (address) {
108 case REG_DISPCNT:
109 value &= 0xFFFB;
110 softwareRenderer->dispcnt.packed = value;
111 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
112 break;
113 case REG_BG0CNT:
114 value &= 0xFFCF;
115 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
116 break;
117 case REG_BG1CNT:
118 value &= 0xFFCF;
119 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
120 break;
121 case REG_BG2CNT:
122 value &= 0xFFCF;
123 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
124 break;
125 case REG_BG3CNT:
126 value &= 0xFFCF;
127 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
128 break;
129 case REG_BG0HOFS:
130 value &= 0x01FF;
131 softwareRenderer->bg[0].x = value;
132 break;
133 case REG_BG0VOFS:
134 value &= 0x01FF;
135 softwareRenderer->bg[0].y = value;
136 break;
137 case REG_BG1HOFS:
138 value &= 0x01FF;
139 softwareRenderer->bg[1].x = value;
140 break;
141 case REG_BG1VOFS:
142 value &= 0x01FF;
143 softwareRenderer->bg[1].y = value;
144 break;
145 case REG_BG2HOFS:
146 value &= 0x01FF;
147 softwareRenderer->bg[2].x = value;
148 break;
149 case REG_BG2VOFS:
150 value &= 0x01FF;
151 softwareRenderer->bg[2].y = value;
152 break;
153 case REG_BG3HOFS:
154 value &= 0x01FF;
155 softwareRenderer->bg[3].x = value;
156 break;
157 case REG_BG3VOFS:
158 value &= 0x01FF;
159 softwareRenderer->bg[3].y = value;
160 break;
161 case REG_BLDCNT:
162 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
163 break;
164 case REG_BLDALPHA:
165 softwareRenderer->blda = value & 0x1F;
166 if (softwareRenderer->blda > 0x10) {
167 softwareRenderer->blda = 0x10;
168 }
169 softwareRenderer->bldb = (value >> 8) & 0x1F;
170 if (softwareRenderer->bldb > 0x10) {
171 softwareRenderer->bldb = 0x10;
172 }
173 break;
174 case REG_BLDY:
175 softwareRenderer->bldy = value & 0x1F;
176 if (softwareRenderer->bldy > 0x10) {
177 softwareRenderer->bldy = 0x10;
178 }
179 _updatePalettes(softwareRenderer);
180 break;
181 default:
182 GBALog(GBA_LOG_STUB, "Stub video register write: %03x", address);
183 }
184 return value;
185}
186
187static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
188 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
189 uint32_t color32 = 0;
190 if (address & 0x1F) {
191 color32 = 0xFF000000;
192 }
193 color32 |= (value << 3) & 0xF8;
194 color32 |= (value << 6) & 0xF800;
195 color32 |= (value << 9) & 0xF80000;
196 softwareRenderer->normalPalette[address >> 1] = color32;
197 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
198 softwareRenderer->variantPalette[address >> 1] = _brighten(color32, softwareRenderer->bldy);
199 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
200 softwareRenderer->variantPalette[address >> 1] = _darken(color32, softwareRenderer->bldy);
201 }
202}
203
204static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
205 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
206 uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
207 if (softwareRenderer->dispcnt.forcedBlank) {
208 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
209 row[x] = GBA_COLOR_WHITE;
210 }
211 return;
212 } else {
213 uint32_t backdrop;
214 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA) {
215 backdrop = softwareRenderer->normalPalette[0];
216 } else {
217 backdrop = softwareRenderer->variantPalette[0];
218 }
219 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
220 row[x] = backdrop;
221 }
222 }
223
224 memset(softwareRenderer->flags, 0, sizeof(softwareRenderer->flags));
225 memset(softwareRenderer->spriteLayer, 0, sizeof(softwareRenderer->spriteLayer));
226 softwareRenderer->row = row;
227
228 softwareRenderer->start = 0;
229 softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
230 _drawScanline(softwareRenderer, y);
231}
232
233static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
234 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
235
236 pthread_mutex_lock(&softwareRenderer->mutex);
237 renderer->framesPending++;
238 pthread_cond_broadcast(&softwareRenderer->upCond);
239 if (!renderer->turbo) {
240 pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
241 }
242 pthread_mutex_unlock(&softwareRenderer->mutex);
243}
244
245static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
246 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
247 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
248 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
249 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
250}
251
252static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
253 (void)(renderer);
254 union GBARegisterBGCNT reg = { .packed = value };
255 bg->priority = reg.priority;
256 bg->charBase = reg.charBase << 14;
257 bg->mosaic = reg.mosaic;
258 bg->multipalette = reg.multipalette;
259 bg->screenBase = reg.screenBase << 11;
260 bg->overflow = reg.overflow;
261 bg->size = reg.size;
262}
263
264static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
265 union {
266 struct {
267 unsigned target1Bg0 : 1;
268 unsigned target1Bg1 : 1;
269 unsigned target1Bg2 : 1;
270 unsigned target1Bg3 : 1;
271 unsigned target1Obj : 1;
272 unsigned target1Bd : 1;
273 enum BlendEffect effect : 2;
274 unsigned target2Bg0 : 1;
275 unsigned target2Bg1 : 1;
276 unsigned target2Bg2 : 1;
277 unsigned target2Bg3 : 1;
278 unsigned target2Obj : 1;
279 unsigned target2Bd : 1;
280 };
281 uint16_t packed;
282 } bldcnt = { .packed = value };
283
284 enum BlendEffect oldEffect = renderer->blendEffect;
285
286 renderer->bg[0].target1 = bldcnt.target1Bg0;
287 renderer->bg[1].target1 = bldcnt.target1Bg1;
288 renderer->bg[2].target1 = bldcnt.target1Bg2;
289 renderer->bg[3].target1 = bldcnt.target1Bg3;
290 renderer->bg[0].target2 = bldcnt.target2Bg0;
291 renderer->bg[1].target2 = bldcnt.target2Bg1;
292 renderer->bg[2].target2 = bldcnt.target2Bg2;
293 renderer->bg[3].target2 = bldcnt.target2Bg3;
294
295 renderer->blendEffect = bldcnt.effect;
296 renderer->target1Obj = bldcnt.target1Obj;
297 renderer->target1Bd = bldcnt.target1Bd;
298 renderer->target2Obj = bldcnt.target2Obj;
299 renderer->target2Bd = bldcnt.target2Bd;
300
301 if (oldEffect != renderer->blendEffect) {
302 _updatePalettes(renderer);
303 }
304}
305
306static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
307 int i;
308 if (renderer->dispcnt.objEnable) {
309 for (i = 0; i < 128; ++i) {
310 struct GBAObj* sprite = &renderer->d.oam->obj[i];
311 if (sprite->transformed) {
312 _drawTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
313 } else if (!sprite->disable) {
314 _drawSprite(renderer, sprite, y);
315 }
316 }
317 }
318
319 int priority;
320 for (priority = 0; priority < 4; ++priority) {
321 for (i = 0; i < 4; ++i) {
322 if (renderer->bg[i].enabled && renderer->bg[i].priority == priority) {
323 _drawBackgroundMode0(renderer, &renderer->bg[i], y);
324 }
325 }
326 }
327}
328
329static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color, struct PixelFlags flags) {
330 struct PixelFlags currentFlags = renderer->flags[offset];
331 if (currentFlags.isSprite && flags.priority >= currentFlags.priority) {
332 if (currentFlags.target1) {
333 if (currentFlags.written && currentFlags.target2) {
334 renderer->row[offset] = _mix(renderer->blda, renderer->row[offset], renderer->bldb, renderer->spriteLayer[offset]);
335 } else if (flags.target2) {
336 renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->spriteLayer[offset]);
337 }
338 } else if (!currentFlags.written) {
339 renderer->row[offset] = renderer->spriteLayer[offset];
340 }
341 renderer->flags[offset].finalized = 1;
342 return;
343 }
344 if (renderer->blendEffect != BLEND_ALPHA) {
345 renderer->row[offset] = color;
346 renderer->flags[offset].finalized = 1;
347 } else if (renderer->blendEffect == BLEND_ALPHA) {
348 if (currentFlags.written) {
349 if (currentFlags.target1 && flags.target2) {
350 renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->row[offset]);
351 }
352 renderer->flags[offset].finalized = 1;
353 } else {
354 renderer->row[offset] = color;
355 renderer->flags[offset].target1 = flags.target1;
356 }
357 }
358 renderer->flags[offset].written = 1;
359}
360
361#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
362 if (tileData & 0xF && !renderer->flags[outX].finalized) { \
363 _composite(renderer, outX, renderer->normalPalette[tileData & 0xF | (mapData.palette << 4)], flags); \
364 } \
365 tileData >>= 4;
366
367#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
368 if (tileData & 0xF && !renderer->flags[outX].finalized) { \
369 _composite(renderer, outX, renderer->variantPalette[tileData & 0xF | (mapData.palette << 4)], flags); \
370 } \
371 tileData >>= 4;
372
373#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
374 if (tileData & 0xFF && !renderer->flags[outX].finalized) { \
375 _composite(renderer, outX, renderer->normalPalette[tileData & 0xFF], flags); \
376 } \
377 tileData >>= 8;
378
379#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
380 if (tileData & 0xFF && !renderer->flags[outX].finalized) { \
381 _composite(renderer, outX, renderer->variantPalette[tileData & 0xFF], flags); \
382 } \
383 tileData >>= 8;
384
385#define BACKGROUND_TEXT_SELECT_CHARACTER \
386 localX = tileX * 8 + inX; \
387 xBase = localX & 0xF8; \
388 if (background->size & 1) { \
389 xBase += (localX & 0x100) << 5; \
390 } \
391 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
392 mapData.packed = renderer->d.vram[screenBase]; \
393 if (!mapData.vflip) { \
394 localY = inY & 0x7; \
395 } else { \
396 localY = 7 - (inY & 0x7); \
397 }
398
399#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
400 for (; tileX < 30; ++tileX) { \
401 BACKGROUND_TEXT_SELECT_CHARACTER; \
402 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
403 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
404 if (tileData) { \
405 if (!mapData.hflip) { \
406 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
407 ++outX; \
408 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
409 ++outX; \
410 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
411 ++outX; \
412 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
413 ++outX; \
414 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
415 ++outX; \
416 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
417 ++outX; \
418 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
419 ++outX; \
420 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
421 ++outX; \
422 } else { \
423 outX += 7; \
424 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
425 --outX; \
426 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
427 --outX; \
428 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
429 --outX; \
430 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
431 --outX; \
432 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
433 --outX; \
434 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
435 --outX; \
436 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
437 --outX; \
438 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
439 outX += 8; \
440 } \
441 } else { \
442 outX += 8; \
443 } \
444 }
445
446#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
447 for (; tileX < 30; ++tileX) { \
448 BACKGROUND_TEXT_SELECT_CHARACTER; \
449 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
450 if (!mapData.hflip) { \
451 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
452 if (tileData) { \
453 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
454 ++outX; \
455 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
456 ++outX; \
457 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
458 ++outX; \
459 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
460 ++outX; \
461 } else { \
462 outX += 4; \
463 } \
464 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
465 if (tileData) { \
466 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
467 ++outX; \
468 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
469 ++outX; \
470 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
471 ++outX; \
472 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
473 ++outX; \
474 } else { \
475 outX += 4; \
476 } \
477 } else { \
478 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
479 if (tileData) { \
480 outX += 3; \
481 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
482 --outX; \
483 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
484 --outX; \
485 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
486 --outX; \
487 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
488 outX += 4; \
489 } else { \
490 outX += 4; \
491 } \
492 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
493 if (tileData) { \
494 outX += 3; \
495 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
496 --outX; \
497 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
498 --outX; \
499 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
500 --outX; \
501 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
502 outX += 4; \
503 } else { \
504 outX += 4; \
505 } \
506 } \
507 }
508
509static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
510 int inX = background->x;
511 int inY = y + background->y;
512 union GBATextMapData mapData;
513
514 unsigned yBase = inY & 0xF8;
515 if (background->size & 2) {
516 yBase += inY & 0x100;
517 } else if (background->size == 3) {
518 yBase += (inY & 0x100) << 1;
519 }
520
521 int localX;
522 int localY;
523
524 unsigned xBase;
525
526 struct PixelFlags flags = {
527 .target1 = background->target1 && renderer->blendEffect == BLEND_ALPHA,
528 .target2 = background->target2,
529 .priority = background->priority
530 };
531
532 uint32_t screenBase;
533 uint32_t charBase;
534 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
535
536 int outX = 0;
537 int tileX = 0;
538 if (inX & 0x7) {
539 int end = 0x8 - (inX & 0x7);
540 uint32_t tileData;
541 BACKGROUND_TEXT_SELECT_CHARACTER;
542 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
543 tileData = ((uint32_t*)renderer->d.vram)[charBase];
544 tileData >>= 4 * (inX & 0x7);
545 if (!variant) {
546 for (outX = 0; outX < end; ++outX) {
547 BACKGROUND_DRAW_PIXEL_16_NORMAL;
548 }
549 } else {
550 for (outX = 0; outX < end; ++outX) {
551 BACKGROUND_DRAW_PIXEL_16_VARIANT;
552 }
553 }
554
555 tileX = 30;
556 BACKGROUND_TEXT_SELECT_CHARACTER;
557 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
558 tileData = ((uint32_t*)renderer->d.vram)[charBase];
559 if (!variant) {
560 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
561 BACKGROUND_DRAW_PIXEL_16_NORMAL;
562 }
563 } else {
564 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
565 BACKGROUND_DRAW_PIXEL_16_VARIANT;
566 }
567 }
568
569 tileX = 1;
570 outX = end;
571 }
572
573 if (!background->multipalette) {
574 if (!variant) {
575 BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
576 } else {
577 BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
578 }
579 } else {
580 if (!variant) {
581 BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
582 } else {
583 BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
584 }
585 }
586}
587
588static const int _objSizes[32] = {
589 8, 8,
590 16, 16,
591 32, 32,
592 64, 64,
593 16, 8,
594 32, 8,
595 32, 16,
596 64, 32,
597 8, 16,
598 8, 32,
599 16, 32,
600 32, 64,
601 0, 0,
602 0, 0,
603 0, 0,
604 0, 0
605};
606
607#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
608 SPRITE_YBASE_ ## DEPTH(inY); \
609 for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) { \
610 int inX = outX - x; \
611 if (sprite->hflip) { \
612 inX = width - inX - 1; \
613 } \
614 if (renderer->flags[outX].isSprite) { \
615 continue; \
616 } \
617 SPRITE_XBASE_ ## DEPTH(inX); \
618 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
619 }
620
621#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
622 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
623 if (renderer->flags[outX].isSprite) { \
624 continue; \
625 } \
626 int inY = y - sprite->y; \
627 int inX = outX - x; \
628 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
629 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
630 \
631 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
632 continue; \
633 } \
634 \
635 SPRITE_YBASE_ ## DEPTH(localY); \
636 SPRITE_XBASE_ ## DEPTH(localX); \
637 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
638 }
639
640#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
641#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
642
643#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
644 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
645 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
646 if (tileData) { \
647 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)]; \
648 renderer->flags[outX] = flags; \
649 }
650
651#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
652 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
653 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
654 if (tileData) { \
655 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)]; \
656 renderer->flags[outX] = flags; \
657 }
658
659#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
660#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x100) + (localY & 0x7) * 8;
661
662#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
663 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
664 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
665 if (tileData) { \
666 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData]; \
667 renderer->flags[outX] = flags; \
668 }
669
670#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
671 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
672 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
673 if (tileData) { \
674 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData]; \
675 renderer->flags[outX] = flags; \
676 }
677
678static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
679 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
680 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
681 int start = renderer->start;
682 int end = renderer->end;
683 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
684 return;
685 }
686 struct PixelFlags flags = {
687 .priority = sprite->priority,
688 .isSprite = 1,
689 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
690 .target2 = renderer->target2Obj
691 };
692 int x = sprite->x;
693 int inY = y - sprite->y;
694 if (sprite->y + height - 256 >= 0) {
695 inY += 256;
696 }
697 if (sprite->vflip) {
698 inY = height - inY - 1;
699 }
700 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
701 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
702 if (!sprite->multipalette) {
703 if (!variant) {
704 SPRITE_NORMAL_LOOP(16, NORMAL);
705 } else {
706 SPRITE_NORMAL_LOOP(16, VARIANT);
707 }
708 } else {
709 if (!variant) {
710 SPRITE_NORMAL_LOOP(256, NORMAL);
711 } else {
712 SPRITE_NORMAL_LOOP(256, VARIANT);
713 }
714 }
715}
716
717static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
718 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
719 int totalWidth = width << sprite->doublesize;
720 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
721 int totalHeight = height << sprite->doublesize;
722 int start = renderer->start;
723 int end = renderer->end;
724 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
725 return;
726 }
727 struct PixelFlags flags = {
728 .priority = sprite->priority,
729 .isSprite = 1,
730 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
731 .target2 = renderer->target2Obj
732 };
733 int x = sprite->x;
734 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
735 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
736 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
737 if (!sprite->multipalette) {
738 if (!variant) {
739 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
740 } else {
741 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
742 }
743 } else {
744 if (!variant) {
745 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
746 } else {
747 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
748 }
749 }
750}
751
752static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
753 if (renderer->blendEffect == BLEND_BRIGHTEN) {
754 for (int i = 0; i < 512; ++i) {
755 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
756 }
757 } else if (renderer->blendEffect == BLEND_DARKEN) {
758 for (int i = 0; i < 512; ++i) {
759 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
760 }
761 } else {
762 for (int i = 0; i < 512; ++i) {
763 renderer->variantPalette[i] = renderer->normalPalette[i];
764 }
765 }
766}
767
768static inline uint32_t _brighten(uint32_t color, int y) {
769 uint32_t c = 0;
770 uint32_t a;
771 a = color & 0xF8;
772 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
773
774 a = color & 0xF800;
775 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
776
777 a = color & 0xF80000;
778 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
779 return c;
780}
781
782static inline uint32_t _darken(uint32_t color, int y) {
783 uint32_t c = 0;
784 uint32_t a;
785 a = color & 0xF8;
786 c |= (a - (a * y) / 16) & 0xF8;
787
788 a = color & 0xF800;
789 c |= (a - (a * y) / 16) & 0xF800;
790
791 a = color & 0xF80000;
792 c |= (a - (a * y) / 16) & 0xF80000;
793 return c;
794}
795
796static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
797 uint32_t c = 0;
798 uint32_t a, b;
799 a = colorA & 0xF8;
800 b = colorB & 0xF8;
801 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
802 if (c & 0x00000100) {
803 c = 0x000000F8;
804 }
805
806 a = colorA & 0xF800;
807 b = colorB & 0xF800;
808 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
809 if (c & 0x00010000) {
810 c = (c & 0x000000F8) | 0x0000F800;
811 }
812
813 a = colorA & 0xF80000;
814 b = colorB & 0xF80000;
815 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
816 if (c & 0x01000000) {
817 c = (c & 0x0000F8F8) | 0x00F80000;
818 }
819 return c;
820}