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_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
626#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
627
628#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
629 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
630 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
631 if (tileData) { \
632 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)]; \
633 renderer->flags[outX] = flags; \
634 }
635
636#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
637 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
638 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
639 if (tileData) { \
640 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)]; \
641 renderer->flags[outX] = flags; \
642 }
643
644#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
645#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x100) + (localY & 0x7) * 8;
646
647#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
648 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
649 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
650 if (tileData) { \
651 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData]; \
652 renderer->flags[outX] = flags; \
653 }
654
655#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
656 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
657 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
658 if (tileData) { \
659 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData]; \
660 renderer->flags[outX] = flags; \
661 }
662
663static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
664 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
665 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
666 int start = renderer->start;
667 int end = renderer->end;
668 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
669 return;
670 }
671 struct PixelFlags flags = {
672 .priority = sprite->priority,
673 .isSprite = 1,
674 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
675 .target2 = renderer->target2Obj
676 };
677 int x = sprite->x;
678 int inY = y - sprite->y;
679 if (sprite->y + height - 256 >= 0) {
680 inY += 256;
681 }
682 if (sprite->vflip) {
683 inY = height - inY - 1;
684 }
685 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
686 int variant = renderer->blendEffect == BLEND_NONE || renderer->blendEffect == BLEND_ALPHA || !renderer->target1Obj;
687 if (!sprite->multipalette) {
688 if (!variant) {
689 SPRITE_NORMAL_LOOP(16, NORMAL);
690 } else {
691 SPRITE_NORMAL_LOOP(16, VARIANT);
692 }
693 } else {
694 if (!variant) {
695 SPRITE_NORMAL_LOOP(256, NORMAL);
696 } else {
697 SPRITE_NORMAL_LOOP(256, VARIANT);
698 }
699 }
700}
701
702static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
703 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
704 int totalWidth = width << sprite->doublesize;
705 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
706 int totalHeight = height << sprite->doublesize;
707 int start = renderer->start;
708 int end = renderer->end;
709 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
710 return;
711 }
712 struct PixelFlags flags = {
713 .priority = sprite->priority,
714 .isSprite = 1,
715 .target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
716 .target2 = renderer->target2Obj
717 };
718 int x = sprite->x;
719 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
720 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
721 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) {
722 if (renderer->flags[outX].isSprite) {
723 continue;
724 }
725 int inY = y - sprite->y;
726 int inX = outX - x;
727 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1);
728 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1);
729
730 if (localX < 0 || localX >= width || localY < 0 || localY >= height) {
731 continue;
732 }
733
734 SPRITE_YBASE_16(localY);
735 SPRITE_XBASE_16(localX);
736 SPRITE_DRAW_PIXEL_16_NORMAL(localX);
737 }
738}
739
740static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
741 if (renderer->blendEffect == BLEND_BRIGHTEN) {
742 for (int i = 0; i < 512; ++i) {
743 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
744 }
745 } else if (renderer->blendEffect == BLEND_DARKEN) {
746 for (int i = 0; i < 512; ++i) {
747 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
748 }
749 } else {
750 for (int i = 0; i < 512; ++i) {
751 renderer->variantPalette[i] = renderer->normalPalette[i];
752 }
753 }
754}
755
756static inline uint32_t _brighten(uint32_t color, int y) {
757 uint32_t c = 0;
758 uint32_t a;
759 a = color & 0xF8;
760 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
761
762 a = color & 0xF800;
763 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
764
765 a = color & 0xF80000;
766 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
767 return c;
768}
769
770static inline uint32_t _darken(uint32_t color, int y) {
771 uint32_t c = 0;
772 uint32_t a;
773 a = color & 0xF8;
774 c |= (a - (a * y) / 16) & 0xF8;
775
776 a = color & 0xF800;
777 c |= (a - (a * y) / 16) & 0xF800;
778
779 a = color & 0xF80000;
780 c |= (a - (a * y) / 16) & 0xF80000;
781 return c;
782}
783
784static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
785 uint32_t c = 0;
786 uint32_t a, b;
787 a = colorA & 0xF8;
788 b = colorB & 0xF8;
789 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
790 if (c & 0x00000100) {
791 c = 0x000000F8;
792 }
793
794 a = colorA & 0xF800;
795 b = colorB & 0xF800;
796 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
797 if (c & 0x00010000) {
798 c = (c & 0x000000F8) | 0x0000F800;
799 }
800
801 a = colorA & 0xF80000;
802 b = colorB & 0xF80000;
803 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
804 if (c & 0x01000000) {
805 c = (c & 0x0000F8F8) | 0x00F80000;
806 }
807 return c;
808}