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