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 uint32_t* row = renderer->row;
308
309 int i;
310 if (renderer->dispcnt.objEnable) {
311 for (i = 0; i < 128; ++i) {
312 struct GBAObj* sprite = &renderer->d.oam->obj[i];
313 if (sprite->transformed) {
314 _drawTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
315 } else if (!sprite->disable) {
316 _drawSprite(renderer, sprite, y);
317 }
318 }
319 }
320
321 int priority;
322 for (priority = 0; priority < 4; ++priority) {
323 for (i = 0; i < 4; ++i) {
324 if (renderer->bg[i].enabled && renderer->bg[i].priority == priority) {
325 _drawBackgroundMode0(renderer, &renderer->bg[i], y);
326 }
327 }
328 }
329}
330
331static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color, struct PixelFlags flags) {
332 struct PixelFlags currentFlags = renderer->flags[offset];
333 if (currentFlags.isSprite && flags.priority >= currentFlags.priority) {
334 if (currentFlags.target1) {
335 if (currentFlags.written && currentFlags.target2) {
336 renderer->row[offset] = _mix(renderer->blda, renderer->row[offset], renderer->bldb, renderer->spriteLayer[offset]);
337 } else if (flags.target2) {
338 renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->spriteLayer[offset]);
339 }
340 } else if (!currentFlags.written) {
341 renderer->row[offset] = renderer->spriteLayer[offset];
342 }
343 renderer->flags[offset].finalized = 1;
344 return;
345 }
346 if (renderer->blendEffect != BLEND_ALPHA) {
347 renderer->row[offset] = color;
348 renderer->flags[offset].finalized = 1;
349 } else if (renderer->blendEffect == BLEND_ALPHA) {
350 if (currentFlags.written) {
351 if (currentFlags.target1 && flags.target2) {
352 renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->row[offset]);
353 }
354 renderer->flags[offset].finalized = 1;
355 } else {
356 renderer->row[offset] = color;
357 renderer->flags[offset].target1 = flags.target1;
358 }
359 }
360 renderer->flags[offset].written = 1;
361}
362
363#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
364 if (tileData & 0xF && !renderer->flags[outX].finalized) { \
365 _composite(renderer, outX, renderer->normalPalette[tileData & 0xF | (mapData.palette << 4)], flags); \
366 } \
367 tileData >>= 4;
368
369#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
370 if (tileData & 0xF && !renderer->flags[outX].finalized) { \
371 _composite(renderer, outX, renderer->variantPalette[tileData & 0xF | (mapData.palette << 4)], flags); \
372 } \
373 tileData >>= 4;
374
375#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
376 if (tileData & 0xFF && !renderer->flags[outX].finalized) { \
377 _composite(renderer, outX, renderer->normalPalette[tileData & 0xFF], flags); \
378 } \
379 tileData >>= 8;
380
381#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
382 if (tileData & 0xFF && !renderer->flags[outX].finalized) { \
383 _composite(renderer, outX, renderer->variantPalette[tileData & 0xFF], flags); \
384 } \
385 tileData >>= 8;
386
387#define BACKGROUND_TEXT_SELECT_CHARACTER \
388 localX = tileX * 8 + inX; \
389 xBase = localX & 0xF8; \
390 if (background->size & 1) { \
391 xBase += (localX & 0x100) << 5; \
392 } \
393 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
394 mapData.packed = renderer->d.vram[screenBase]; \
395 if (!mapData.vflip) { \
396 localY = inY & 0x7; \
397 } else { \
398 localY = 7 - (inY & 0x7); \
399 }
400
401#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
402 for (tileX; tileX < 30; ++tileX) { \
403 BACKGROUND_TEXT_SELECT_CHARACTER; \
404 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
405 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
406 if (tileData) { \
407 if (!mapData.hflip) { \
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 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
423 ++outX; \
424 } else { \
425 outX += 7; \
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; \
440 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
441 outX += 8; \
442 } \
443 } else { \
444 outX += 8; \
445 } \
446 }
447
448#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
449 for (tileX; tileX < 30; ++tileX) { \
450 BACKGROUND_TEXT_SELECT_CHARACTER; \
451 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
452 if (!mapData.hflip) { \
453 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
454 if (tileData) { \
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 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
462 ++outX; \
463 } else { \
464 outX += 4; \
465 } \
466 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
467 if (tileData) { \
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 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
475 ++outX; \
476 } else { \
477 outX += 4; \
478 } \
479 } else { \
480 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
481 if (tileData) { \
482 outX += 3; \
483 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
484 --outX; \
485 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
486 --outX; \
487 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
488 --outX; \
489 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
490 outX += 4; \
491 } else { \
492 outX += 4; \
493 } \
494 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
495 if (tileData) { \
496 outX += 3; \
497 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
498 --outX; \
499 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
500 --outX; \
501 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
502 --outX; \
503 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
504 outX += 4; \
505 } else { \
506 outX += 4; \
507 } \
508 } \
509 }
510
511static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
512 int start = renderer->start;
513 int end = renderer->end;
514 int inX = start + background->x;
515 int inY = y + background->y;
516 union GBATextMapData mapData;
517
518 unsigned yBase = inY & 0xF8;
519 if (background->size & 2) {
520 yBase += inY & 0x100;
521 } else if (background->size == 3) {
522 yBase += (inY & 0x100) << 1;
523 }
524
525 int localX;
526 int localY;
527
528 unsigned xBase;
529
530 struct PixelFlags flags = {
531 .target1 = background->target1 && renderer->blendEffect == BLEND_ALPHA,
532 .target2 = background->target2,
533 .priority = background->priority
534 };
535
536 uint32_t screenBase;
537 uint32_t charBase;
538 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
539
540 int outX = 0;
541 int tileX = 0;
542 if (inX & 0x7) {
543 int end = 0x8 - (inX & 0x7);
544 uint32_t tileData;
545 BACKGROUND_TEXT_SELECT_CHARACTER;
546 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
547 tileData = ((uint32_t*)renderer->d.vram)[charBase];
548 tileData >>= 4 * (inX & 0x7);
549 if (!variant) {
550 for (outX = 0; outX < end; ++outX) {
551 BACKGROUND_DRAW_PIXEL_16_NORMAL;
552 }
553 } else {
554 for (outX = 0; outX < end; ++outX) {
555 BACKGROUND_DRAW_PIXEL_16_VARIANT;
556 }
557 }
558
559 tileX = 30;
560 BACKGROUND_TEXT_SELECT_CHARACTER;
561 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
562 tileData = ((uint32_t*)renderer->d.vram)[charBase];
563 if (!variant) {
564 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
565 BACKGROUND_DRAW_PIXEL_16_NORMAL;
566 }
567 } else {
568 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
569 BACKGROUND_DRAW_PIXEL_16_VARIANT;
570 }
571 }
572
573 tileX = 1;
574 outX = end;
575 }
576
577 if (!background->multipalette) {
578 if (!variant) {
579 BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
580 } else {
581 BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
582 }
583 } else {
584 if (!variant) {
585 BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
586 } else {
587 BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
588 }
589 }
590}
591
592static const int _objSizes[32] = {
593 8, 8,
594 16, 16,
595 32, 32,
596 64, 64,
597 16, 8,
598 32, 8,
599 32, 16,
600 64, 32,
601 8, 16,
602 8, 32,
603 16, 32,
604 32, 64,
605 0, 0,
606 0, 0,
607 0, 0,
608 0, 0
609};
610
611#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
612 SPRITE_YBASE_ ## DEPTH(inY); \
613 for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) { \
614 int inX = outX - x; \
615 if (sprite->hflip) { \
616 inX = width - inX - 1; \
617 } \
618 if (renderer->flags[outX].isSprite) { \
619 continue; \
620 } \
621 SPRITE_XBASE_ ## DEPTH(inX); \
622 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
623 }
624
625#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
626 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
627 if (renderer->flags[outX].isSprite) { \
628 continue; \
629 } \
630 int inY = y - sprite->y; \
631 int inX = outX - x; \
632 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
633 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
634 \
635 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
636 continue; \
637 } \
638 \
639 SPRITE_YBASE_ ## DEPTH(localY); \
640 SPRITE_XBASE_ ## DEPTH(localX); \
641 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
642 }
643
644#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
645#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
646
647#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
648 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
649 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
650 if (tileData) { \
651 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)]; \
652 renderer->flags[outX] = flags; \
653 }
654
655#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
656 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
657 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
658 if (tileData) { \
659 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)]; \
660 renderer->flags[outX] = flags; \
661 }
662
663#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
664#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x100) + (localY & 0x7) * 8;
665
666#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
667 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
668 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
669 if (tileData) { \
670 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData]; \
671 renderer->flags[outX] = flags; \
672 }
673
674#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
675 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
676 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
677 if (tileData) { \
678 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData]; \
679 renderer->flags[outX] = flags; \
680 }
681
682static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
683 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
684 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
685 int start = renderer->start;
686 int end = renderer->end;
687 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
688 return;
689 }
690 struct PixelFlags flags = {
691 .priority = sprite->priority,
692 .isSprite = 1,
693 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
694 .target2 = renderer->target2Obj
695 };
696 int x = sprite->x;
697 int inY = y - sprite->y;
698 if (sprite->y + height - 256 >= 0) {
699 inY += 256;
700 }
701 if (sprite->vflip) {
702 inY = height - inY - 1;
703 }
704 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
705 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
706 if (!sprite->multipalette) {
707 if (!variant) {
708 SPRITE_NORMAL_LOOP(16, NORMAL);
709 } else {
710 SPRITE_NORMAL_LOOP(16, VARIANT);
711 }
712 } else {
713 if (!variant) {
714 SPRITE_NORMAL_LOOP(256, NORMAL);
715 } else {
716 SPRITE_NORMAL_LOOP(256, VARIANT);
717 }
718 }
719}
720
721static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
722 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
723 int totalWidth = width << sprite->doublesize;
724 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
725 int totalHeight = height << sprite->doublesize;
726 int start = renderer->start;
727 int end = renderer->end;
728 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
729 return;
730 }
731 struct PixelFlags flags = {
732 .priority = sprite->priority,
733 .isSprite = 1,
734 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
735 .target2 = renderer->target2Obj
736 };
737 int x = sprite->x;
738 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
739 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
740 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
741 if (!sprite->multipalette) {
742 if (!variant) {
743 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
744 } else {
745 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
746 }
747 } else {
748 if (!variant) {
749 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
750 } else {
751 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
752 }
753 }
754}
755
756static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
757 if (renderer->blendEffect == BLEND_BRIGHTEN) {
758 for (int i = 0; i < 512; ++i) {
759 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
760 }
761 } else if (renderer->blendEffect == BLEND_DARKEN) {
762 for (int i = 0; i < 512; ++i) {
763 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
764 }
765 } else {
766 for (int i = 0; i < 512; ++i) {
767 renderer->variantPalette[i] = renderer->normalPalette[i];
768 }
769 }
770}
771
772static inline uint32_t _brighten(uint32_t color, int y) {
773 uint32_t c = 0;
774 uint32_t a;
775 a = color & 0xF8;
776 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
777
778 a = color & 0xF800;
779 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
780
781 a = color & 0xF80000;
782 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
783 return c;
784}
785
786static inline uint32_t _darken(uint32_t color, int y) {
787 uint32_t c = 0;
788 uint32_t a;
789 a = color & 0xF8;
790 c |= (a - (a * y) / 16) & 0xF8;
791
792 a = color & 0xF800;
793 c |= (a - (a * y) / 16) & 0xF800;
794
795 a = color & 0xF80000;
796 c |= (a - (a * y) / 16) & 0xF80000;
797 return c;
798}
799
800static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
801 uint32_t c = 0;
802 uint32_t a, b;
803 a = colorA & 0xF8;
804 b = colorB & 0xF8;
805 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
806 if (c & 0x00000100) {
807 c = 0x000000F8;
808 }
809
810 a = colorA & 0xF800;
811 b = colorB & 0xF800;
812 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
813 if (c & 0x00010000) {
814 c = (c & 0x000000F8) | 0x0000F800;
815 }
816
817 a = colorA & 0xF80000;
818 b = colorB & 0xF80000;
819 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
820 if (c & 0x01000000) {
821 c = (c & 0x0000F8F8) | 0x00F80000;
822 }
823 return c;
824}