src/gba/renderers/video-software.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "video-software.h"
7
8#include "gba/gba.h"
9#include "gba/io.h"
10
11#include "util/arm-algo.h"
12
13#ifdef NDEBUG
14#define VIDEO_CHECKS false
15#else
16#define VIDEO_CHECKS true
17#endif
18
19static const int _objSizes[32] = {
20 8, 8,
21 16, 16,
22 32, 32,
23 64, 64,
24 16, 8,
25 32, 8,
26 32, 16,
27 64, 32,
28 8, 16,
29 8, 32,
30 16, 32,
31 32, 64,
32 0, 0,
33 0, 0,
34 0, 0,
35 0, 0
36};
37
38static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
39static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
40static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
41static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
42static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
43static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
44static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
45static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels);
46static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels);
47
48static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
49static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
50static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
51static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
52static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
53static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
54static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
55static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
56static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
57static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
58static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
59
60static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
61static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
62static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
63static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
64static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
65static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
66static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
67static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
68static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
69
70static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
71static inline unsigned _brighten(unsigned color, int y);
72static inline unsigned _darken(unsigned color, int y);
73static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);
74
75static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y);
76static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win);
77
78void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
79 renderer->d.init = GBAVideoSoftwareRendererInit;
80 renderer->d.reset = GBAVideoSoftwareRendererInit;
81 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
82 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
83 renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
84 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
85 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
86 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
87 renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels;
88 renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels;
89}
90
91static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
92 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
93 int i;
94
95 softwareRenderer->dispcnt = 0x0080;
96
97 softwareRenderer->target1Obj = 0;
98 softwareRenderer->target1Bd = 0;
99 softwareRenderer->target2Obj = 0;
100 softwareRenderer->target2Bd = 0;
101 softwareRenderer->blendEffect = BLEND_NONE;
102 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
103 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
104
105 softwareRenderer->blda = 0;
106 softwareRenderer->bldb = 0;
107 softwareRenderer->bldy = 0;
108
109 softwareRenderer->winN[0] = (struct WindowN) { .control = { .priority = 0 } };
110 softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } };
111 softwareRenderer->objwin = (struct WindowControl) { .priority = 2 };
112 softwareRenderer->winout = (struct WindowControl) { .priority = 3 };
113 softwareRenderer->oamMax = 0;
114
115 softwareRenderer->mosaic = 0;
116
117 for (i = 0; i < 4; ++i) {
118 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
119 bg->index = i;
120 bg->enabled = 0;
121 bg->priority = 0;
122 bg->charBase = 0;
123 bg->mosaic = 0;
124 bg->multipalette = 0;
125 bg->screenBase = 0;
126 bg->overflow = 0;
127 bg->size = 0;
128 bg->target1 = 0;
129 bg->target2 = 0;
130 bg->x = 0;
131 bg->y = 0;
132 bg->refx = 0;
133 bg->refy = 0;
134 bg->dx = 256;
135 bg->dmx = 0;
136 bg->dy = 0;
137 bg->dmy = 256;
138 bg->sx = 0;
139 bg->sy = 0;
140 }
141}
142
143static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
144 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
145 UNUSED(softwareRenderer);
146}
147
148static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
149 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
150 switch (address) {
151 case REG_DISPCNT:
152 softwareRenderer->dispcnt = value;
153 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
154 break;
155 case REG_BG0CNT:
156 value &= 0xFFCF;
157 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
158 break;
159 case REG_BG1CNT:
160 value &= 0xFFCF;
161 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
162 break;
163 case REG_BG2CNT:
164 value &= 0xFFCF;
165 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
166 break;
167 case REG_BG3CNT:
168 value &= 0xFFCF;
169 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
170 break;
171 case REG_BG0HOFS:
172 value &= 0x01FF;
173 softwareRenderer->bg[0].x = value;
174 break;
175 case REG_BG0VOFS:
176 value &= 0x01FF;
177 softwareRenderer->bg[0].y = value;
178 break;
179 case REG_BG1HOFS:
180 value &= 0x01FF;
181 softwareRenderer->bg[1].x = value;
182 break;
183 case REG_BG1VOFS:
184 value &= 0x01FF;
185 softwareRenderer->bg[1].y = value;
186 break;
187 case REG_BG2HOFS:
188 value &= 0x01FF;
189 softwareRenderer->bg[2].x = value;
190 break;
191 case REG_BG2VOFS:
192 value &= 0x01FF;
193 softwareRenderer->bg[2].y = value;
194 break;
195 case REG_BG3HOFS:
196 value &= 0x01FF;
197 softwareRenderer->bg[3].x = value;
198 break;
199 case REG_BG3VOFS:
200 value &= 0x01FF;
201 softwareRenderer->bg[3].y = value;
202 break;
203 case REG_BG2PA:
204 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
205 break;
206 case REG_BG2PB:
207 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
208 break;
209 case REG_BG2PC:
210 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
211 break;
212 case REG_BG2PD:
213 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
214 break;
215 case REG_BG2X_LO:
216 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
217 break;
218 case REG_BG2X_HI:
219 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
220 break;
221 case REG_BG2Y_LO:
222 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
223 break;
224 case REG_BG2Y_HI:
225 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
226 break;
227 case REG_BG3PA:
228 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
229 break;
230 case REG_BG3PB:
231 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
232 break;
233 case REG_BG3PC:
234 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
235 break;
236 case REG_BG3PD:
237 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
238 break;
239 case REG_BG3X_LO:
240 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
241 break;
242 case REG_BG3X_HI:
243 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
244 break;
245 case REG_BG3Y_LO:
246 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
247 break;
248 case REG_BG3Y_HI:
249 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
250 break;
251 case REG_BLDCNT:
252 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
253 break;
254 case REG_BLDALPHA:
255 softwareRenderer->blda = value & 0x1F;
256 if (softwareRenderer->blda > 0x10) {
257 softwareRenderer->blda = 0x10;
258 }
259 softwareRenderer->bldb = (value >> 8) & 0x1F;
260 if (softwareRenderer->bldb > 0x10) {
261 softwareRenderer->bldb = 0x10;
262 }
263 break;
264 case REG_BLDY:
265 softwareRenderer->bldy = value & 0x1F;
266 if (softwareRenderer->bldy > 0x10) {
267 softwareRenderer->bldy = 0x10;
268 }
269 _updatePalettes(softwareRenderer);
270 break;
271 case REG_WIN0H:
272 softwareRenderer->winN[0].h.end = value;
273 softwareRenderer->winN[0].h.start = value >> 8;
274 if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
275 softwareRenderer->winN[0].h.start = 0;
276 }
277 if (softwareRenderer->winN[0].h.end > VIDEO_HORIZONTAL_PIXELS) {
278 softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS;
279 if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS) {
280 softwareRenderer->winN[0].h.start = VIDEO_HORIZONTAL_PIXELS;
281 }
282 }
283 break;
284 case REG_WIN1H:
285 softwareRenderer->winN[1].h.end = value;
286 softwareRenderer->winN[1].h.start = value >> 8;
287 if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
288 softwareRenderer->winN[1].h.start = 0;
289 }
290 if (softwareRenderer->winN[1].h.end > VIDEO_HORIZONTAL_PIXELS) {
291 softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS;
292 if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS) {
293 softwareRenderer->winN[1].h.start = VIDEO_HORIZONTAL_PIXELS;
294 }
295 }
296 break;
297 case REG_WIN0V:
298 softwareRenderer->winN[0].v.end = value;
299 softwareRenderer->winN[0].v.start = value >> 8;
300 if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
301 softwareRenderer->winN[0].v.start = 0;
302 }
303 if (softwareRenderer->winN[0].v.end > VIDEO_VERTICAL_PIXELS) {
304 softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
305 if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS) {
306 softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS;
307 }
308 }
309 break;
310 case REG_WIN1V:
311 softwareRenderer->winN[1].v.end = value;
312 softwareRenderer->winN[1].v.start = value >> 8;
313 if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
314 softwareRenderer->winN[1].v.start = 0;
315 }
316 if (softwareRenderer->winN[1].v.end > VIDEO_VERTICAL_PIXELS) {
317 softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
318 if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS) {
319 softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS;
320 }
321 }
322 break;
323 case REG_WININ:
324 softwareRenderer->winN[0].control.packed = value;
325 softwareRenderer->winN[1].control.packed = value >> 8;
326 break;
327 case REG_WINOUT:
328 softwareRenderer->winout.packed = value;
329 softwareRenderer->objwin.packed = value >> 8;
330 break;
331 case REG_MOSAIC:
332 softwareRenderer->mosaic = value;
333 break;
334 case REG_GREENSWP:
335 GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);
336 break;
337 default:
338 GBALog(0, GBA_LOG_GAME_ERROR, "Invalid video register: 0x%03X", address);
339 }
340 return value;
341}
342
343static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
344 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
345 softwareRenderer->oamDirty = 1;
346 UNUSED(oam);
347}
348
349static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
350 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
351#ifdef COLOR_16_BIT
352#ifdef COLOR_5_6_5
353 unsigned color = 0;
354 color |= (value & 0x001F) << 11;
355 color |= (value & 0x03E0) << 1;
356 color |= (value & 0x7C00) >> 10;
357#else
358 unsigned color = value;
359#endif
360#else
361 unsigned color = 0;
362 color |= (value << 3) & 0xF8;
363 color |= (value << 6) & 0xF800;
364 color |= (value << 9) & 0xF80000;
365#endif
366 softwareRenderer->normalPalette[address >> 1] = color;
367 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
368 softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
369 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
370 softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
371 }
372}
373
374static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {
375 if (win->v.end >= win->v.start) {
376 if (y >= win->v.end) {
377 return;
378 }
379 if (y < win->v.start) {
380 return;
381 }
382 } else if (y >= win->v.end && y < win->v.start) {
383 return;
384 }
385 if (win->h.end > VIDEO_HORIZONTAL_PIXELS || win->h.end < win->h.start) {
386 struct WindowN splits[2] = { *win, *win };
387 splits[0].h.start = 0;
388 splits[1].h.end = VIDEO_HORIZONTAL_PIXELS;
389 _breakWindowInner(softwareRenderer, &splits[0]);
390 _breakWindowInner(softwareRenderer, &splits[1]);
391 } else {
392 _breakWindowInner(softwareRenderer, win);
393 }
394}
395
396static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
397 int activeWindow;
398 int startX = 0;
399 if (win->h.end > 0) {
400 for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
401 if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
402 // Insert a window before the end of the active window
403 struct Window oldWindow = softwareRenderer->windows[activeWindow];
404 if (win->h.start > startX) {
405 // And after the start of the active window
406 int nextWindow = softwareRenderer->nWindows;
407 ++softwareRenderer->nWindows;
408 for (; nextWindow > activeWindow; --nextWindow) {
409 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
410 }
411 softwareRenderer->windows[activeWindow].endX = win->h.start;
412 ++activeWindow;
413 }
414 softwareRenderer->windows[activeWindow].control = win->control;
415 softwareRenderer->windows[activeWindow].endX = win->h.end;
416 if (win->h.end >= oldWindow.endX) {
417 // Trim off extra windows we've overwritten
418 for (++activeWindow; softwareRenderer->nWindows > activeWindow + 1 && win->h.end >= softwareRenderer->windows[activeWindow].endX; ++activeWindow) {
419#ifdef DEBUG
420 if (activeWindow >= MAX_WINDOW) {
421 GBALog(0, GBA_LOG_DANGER, "Out of bounds window write will occur");
422 return;
423 }
424#endif
425 softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
426 --softwareRenderer->nWindows;
427 }
428 } else {
429 ++activeWindow;
430 int nextWindow = softwareRenderer->nWindows;
431 ++softwareRenderer->nWindows;
432 for (; nextWindow > activeWindow; --nextWindow) {
433 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
434 }
435 softwareRenderer->windows[activeWindow] = oldWindow;
436 }
437 break;
438 }
439 startX = softwareRenderer->windows[activeWindow].endX;
440 }
441 }
442#ifdef DEBUG
443 if (softwareRenderer->nWindows > MAX_WINDOW) {
444 GBALog(0, GBA_LOG_ABORT, "Out of bounds window write occurred!");
445 }
446#endif
447}
448
449static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
450 int i;
451 int oamMax = 0;
452 for (i = 0; i < 128; ++i) {
453 struct GBAObj obj;
454 LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a);
455 LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b);
456 LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c);
457 if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
458 int height = _objSizes[GBAObjAttributesAGetShape(obj.a) * 8 + GBAObjAttributesBGetSize(obj.b) * 2 + 1];
459 if (GBAObjAttributesAIsTransformed(obj.a)) {
460 height <<= GBAObjAttributesAGetDoubleSize(obj.a);
461 }
462 if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
463 renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a);
464 renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height;
465 renderer->sprites[oamMax].obj = obj;
466 ++oamMax;
467 }
468 }
469 }
470 renderer->oamMax = oamMax;
471 renderer->oamDirty = 0;
472}
473
474
475static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
476 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
477
478 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
479 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
480 int x;
481 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
482 row[x] = GBA_COLOR_WHITE;
483 }
484 return;
485 }
486
487 int x;
488 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; x += 4) {
489 softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
490 softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
491 softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
492 softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
493 }
494
495 softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
496 softwareRenderer->nWindows = 1;
497 if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
498 softwareRenderer->windows[0].control = softwareRenderer->winout;
499 if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt)) {
500 _breakWindow(softwareRenderer, &softwareRenderer->winN[1], y);
501 }
502 if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt)) {
503 _breakWindow(softwareRenderer, &softwareRenderer->winN[0], y);
504 }
505 } else {
506 softwareRenderer->windows[0].control.packed = 0xFF;
507 }
508
509 int w;
510 x = 0;
511 for (w = 0; w < softwareRenderer->nWindows; ++w) {
512 // TOOD: handle objwin on backdrop
513 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
514 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
515 backdrop |= softwareRenderer->normalPalette[0];
516 } else {
517 backdrop |= softwareRenderer->variantPalette[0];
518 }
519 int end = softwareRenderer->windows[w].endX;
520 for (; x < end; ++x) {
521 softwareRenderer->row[x] = backdrop;
522 }
523 }
524
525 _drawScanline(softwareRenderer, y);
526
527 if (softwareRenderer->target2Bd) {
528 x = 0;
529 for (w = 0; w < softwareRenderer->nWindows; ++w) {
530 uint32_t backdrop = FLAG_UNWRITTEN;
531 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
532 backdrop |= softwareRenderer->normalPalette[0];
533 } else {
534 backdrop |= softwareRenderer->variantPalette[0];
535 }
536 int end = softwareRenderer->windows[w].endX;
537 for (; x < end; ++x) {
538 uint32_t color = softwareRenderer->row[x];
539 if (color & FLAG_TARGET_1) {
540 softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
541 }
542 }
543 }
544 }
545
546#ifdef COLOR_16_BIT
547#ifdef __arm__
548 _to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
549#else
550 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
551 row[x] = softwareRenderer->row[x];
552 }
553#endif
554#else
555 memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
556#endif
557}
558
559static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
560 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
561
562 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
563 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
564 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
565 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
566}
567
568static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
569 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
570
571 *stride = softwareRenderer->outputBufferStride;
572 *pixels = softwareRenderer->outputBuffer;
573}
574
575static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
576 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
577
578 uint32_t* colorPixels = pixels;
579 unsigned i;
580 for (i = 0; i < VIDEO_VERTICAL_PIXELS; ++i) {
581 memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
582 }
583}
584
585static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
586 renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt);
587 renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt);
588 renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt);
589 renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt);
590}
591
592static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
593 UNUSED(renderer);
594 bg->priority = GBARegisterBGCNTGetPriority(value);
595 bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14;
596 bg->mosaic = GBARegisterBGCNTGetMosaic(value);
597 bg->multipalette = GBARegisterBGCNTGet256Color(value);
598 bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 11;
599 bg->overflow = GBARegisterBGCNTGetOverflow(value);
600 bg->size = GBARegisterBGCNTGetSize(value);
601}
602
603static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
604 bg->dx = value;
605}
606
607static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
608 bg->dmx = value;
609}
610
611static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
612 bg->dy = value;
613}
614
615static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
616 bg->dmy = value;
617}
618
619static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
620 bg->refx = (bg->refx & 0xFFFF0000) | value;
621 bg->sx = bg->refx;
622}
623
624static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
625 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
626 bg->refx <<= 4;
627 bg->refx >>= 4;
628 bg->sx = bg->refx;
629}
630
631static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
632 bg->refy = (bg->refy & 0xFFFF0000) | value;
633 bg->sy = bg->refy;
634}
635
636static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
637 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
638 bg->refy <<= 4;
639 bg->refy >>= 4;
640 bg->sy = bg->refy;
641}
642
643static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
644 enum BlendEffect oldEffect = renderer->blendEffect;
645
646 renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
647 renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
648 renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
649 renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
650 renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
651 renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
652 renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
653 renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
654
655 renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
656 renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
657 renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
658 renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
659 renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
660
661 renderer->anyTarget2 = value & 0x3F00;
662
663 if (oldEffect != renderer->blendEffect) {
664 _updatePalettes(renderer);
665 }
666}
667
668#define TEST_LAYER_ENABLED(X) \
669 (renderer->bg[X].enabled && \
670 (GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \
671 (GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \
672 renderer->bg[X].priority == priority)
673
674static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
675 int w;
676 renderer->end = 0;
677 int spriteLayers = 0;
678 if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt)) {
679 if (renderer->oamDirty) {
680 _cleanOAM(renderer);
681 }
682 int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
683 int mosaicY = y - (y % mosaicV);
684 for (w = 0; w < renderer->nWindows; ++w) {
685 renderer->start = renderer->end;
686 renderer->end = renderer->windows[w].endX;
687 renderer->currentWindow = renderer->windows[w].control;
688 if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
689 continue;
690 }
691 int i;
692 int drawn;
693 for (i = 0; i < renderer->oamMax; ++i) {
694 int localY = y;
695 struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
696 if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
697 localY = mosaicY;
698 }
699 if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
700 continue;
701 }
702 drawn = _preprocessSprite(renderer, &sprite->obj, localY);
703 spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
704 }
705 }
706 }
707
708 int priority;
709 for (priority = 0; priority < 4; ++priority) {
710 renderer->end = 0;
711 for (w = 0; w < renderer->nWindows; ++w) {
712 renderer->start = renderer->end;
713 renderer->end = renderer->windows[w].endX;
714 renderer->currentWindow = renderer->windows[w].control;
715 if (spriteLayers & (1 << priority)) {
716 _postprocessSprite(renderer, priority);
717 }
718 if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
719 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
720 }
721 if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
722 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
723 }
724 if (TEST_LAYER_ENABLED(2)) {
725 switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
726 case 0:
727 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
728 break;
729 case 1:
730 case 2:
731 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
732 break;
733 case 3:
734 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
735 break;
736 case 4:
737 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
738 break;
739 case 5:
740 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
741 break;
742 }
743 }
744 if (TEST_LAYER_ENABLED(3)) {
745 switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
746 case 0:
747 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
748 break;
749 case 2:
750 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
751 break;
752 }
753 }
754 }
755 }
756 renderer->bg[2].sx += renderer->bg[2].dmx;
757 renderer->bg[2].sy += renderer->bg[2].dmy;
758 renderer->bg[3].sx += renderer->bg[3].dmx;
759 renderer->bg[3].sy += renderer->bg[3].dmy;
760}
761
762// We stash the priority on the top bits so we can do a one-operator comparison
763// The lower the number, the higher the priority, and sprites take precendence over backgrounds
764// We want to do special processing if the color pixel is target 1, however
765
766static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
767 if (color >= current) {
768 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
769 color = _mix(renderer->blda, current, renderer->bldb, color);
770 } else {
771 color = current & 0x00FFFFFF;
772 }
773 } else {
774 color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
775 }
776 *pixel = color;
777}
778
779static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
780 // We stash the priority on the top bits so we can do a one-operator comparison
781 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
782 // We want to do special processing if the color pixel is target 1, however
783 if (color >= current) {
784 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
785 color = _mix(renderer->blda, current, renderer->bldb, color);
786 } else {
787 color = current & 0x00FFFFFF;
788 }
789 } else {
790 color = color & ~FLAG_TARGET_2;
791 }
792 *pixel = color;
793}
794
795static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
796 UNUSED(renderer);
797 if (color < current) {
798 *pixel = color | (current & FLAG_OBJWIN);
799 }
800}
801
802static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
803 UNUSED(renderer);
804 if (color < current) {
805 *pixel = color;
806 }
807}
808
809#define COMPOSITE_16_OBJWIN(BLEND) \
810 if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
811 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
812 unsigned mergedFlags = flags; \
813 if (current & FLAG_OBJWIN) { \
814 mergedFlags = objwinFlags; \
815 } \
816 _composite ## BLEND ## Objwin(renderer, pixel, color | mergedFlags, current); \
817 }
818
819#define COMPOSITE_16_NO_OBJWIN(BLEND) \
820 _composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
821
822#define COMPOSITE_256_OBJWIN(BLEND) \
823 if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
824 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
825 unsigned mergedFlags = flags; \
826 if (current & FLAG_OBJWIN) { \
827 mergedFlags = objwinFlags; \
828 } \
829 _composite ## BLEND ## Objwin(renderer, pixel, color | mergedFlags, current); \
830 }
831
832#define COMPOSITE_256_NO_OBJWIN(BLEND) \
833 COMPOSITE_16_NO_OBJWIN(BLEND)
834
835#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \
836 pixelData = tileData & 0xF; \
837 current = *pixel; \
838 if (pixelData && IS_WRITABLE(current)) { \
839 COMPOSITE_16_ ## OBJWIN (BLEND); \
840 } \
841 tileData >>= 4;
842
843#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN) \
844 pixelData = tileData & 0xFF; \
845 current = *pixel; \
846 if (pixelData && IS_WRITABLE(current)) { \
847 COMPOSITE_256_ ## OBJWIN (BLEND); \
848 } \
849 tileData >>= 8;
850
851#define BACKGROUND_TEXT_SELECT_CHARACTER \
852 localX = tileX * 8 + inX; \
853 xBase = localX & 0xF8; \
854 if (background->size & 1) { \
855 xBase += (localX & 0x100) << 5; \
856 } \
857 screenBase = yBase + (xBase >> 3); \
858 LOAD_16(mapData, screenBase << 1, vram); \
859 localY = inY & 0x7; \
860 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
861 localY = 7 - localY; \
862 }
863
864// TODO: Remove UNUSEDs after implementing OBJWIN for modes 3 - 5
865#define PREPARE_OBJWIN \
866 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \
867 int objwinOnly = 0; \
868 int objwinForceEnable = 0; \
869 UNUSED(objwinForceEnable); \
870 color_t* objwinPalette = renderer->normalPalette; \
871 UNUSED(objwinPalette); \
872 if (objwinSlowPath) { \
873 if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
874 objwinPalette = renderer->variantPalette; \
875 } \
876 switch (background->index) { \
877 case 0: \
878 objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
879 objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
880 break; \
881 case 1: \
882 objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
883 objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
884 break; \
885 case 2: \
886 objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
887 objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
888 break; \
889 case 3: \
890 objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
891 objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
892 break; \
893 } \
894 }
895
896#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
897 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
898 palette = &mainPalette[paletteData]; \
899 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
900 LOAD_32(tileData, charBase, vram); \
901 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
902 tileData >>= 4 * mod8; \
903 for (; outX < end; ++outX, ++pixel) { \
904 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
905 } \
906 } else { \
907 for (outX = end - 1; outX >= renderer->start; --outX) { \
908 uint32_t* pixel = &renderer->row[outX]; \
909 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
910 } \
911 }
912
913#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
914 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
915 LOAD_32(tileData, charBase, vram); \
916 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
917 palette = &mainPalette[paletteData]; \
918 pixel = &renderer->row[outX]; \
919 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
920 if (outX < renderer->start) { \
921 tileData >>= 4 * (renderer->start - outX); \
922 outX = renderer->start; \
923 pixel = &renderer->row[outX]; \
924 } \
925 for (; outX < renderer->end; ++outX, ++pixel) { \
926 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
927 } \
928 } else { \
929 tileData >>= 4 * (0x8 - mod8); \
930 int end = renderer->end - 8; \
931 if (end < -1) { \
932 end = -1; \
933 } \
934 outX = renderer->end - 1; \
935 pixel = &renderer->row[outX]; \
936 for (; outX > end; --outX, --pixel) { \
937 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
938 } \
939 /* Needed for consistency checks */ \
940 if (VIDEO_CHECKS) { \
941 outX = renderer->end; \
942 pixel = &renderer->row[outX]; \
943 } \
944 }
945
946#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
947 x = inX & 7; \
948 if (mosaicWait) { \
949 int baseX = x - (mosaicH - mosaicWait); \
950 if (baseX < 0) { \
951 int disturbX = (16 + baseX) >> 3; \
952 inX -= disturbX << 3; \
953 BACKGROUND_TEXT_SELECT_CHARACTER; \
954 baseX -= disturbX << 3; \
955 inX += disturbX << 3; \
956 } else { \
957 BACKGROUND_TEXT_SELECT_CHARACTER; \
958 } \
959 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
960 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
961 palette = &mainPalette[paletteData]; \
962 LOAD_32(tileData, charBase, vram); \
963 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
964 tileData >>= 4 * baseX; \
965 } else { \
966 tileData >>= 4 * (7 - baseX); \
967 } \
968 tileData &= 0xF; \
969 tileData |= tileData << 4; \
970 tileData |= tileData << 8; \
971 tileData |= tileData << 12; \
972 tileData |= tileData << 16; \
973 tileData |= tileData << 20; \
974 tileData |= tileData << 24; \
975 tileData |= tileData << 28; \
976 carryData = tileData; \
977 } \
978 for (; length; ++tileX) { \
979 BACKGROUND_TEXT_SELECT_CHARACTER; \
980 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
981 tileData = carryData; \
982 for (; x < 8 && length; ++x, --length) { \
983 if (!mosaicWait) { \
984 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
985 palette = &mainPalette[paletteData]; \
986 LOAD_32(tileData, charBase, vram); \
987 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
988 tileData >>= x * 4; \
989 } else { \
990 tileData >>= (7 - x) * 4; \
991 } \
992 tileData &= 0xF; \
993 tileData |= tileData << 4; \
994 tileData |= tileData << 8; \
995 tileData |= tileData << 12; \
996 tileData |= tileData << 16; \
997 tileData |= tileData << 20; \
998 tileData |= tileData << 24; \
999 tileData |= tileData << 28; \
1000 carryData = tileData; \
1001 mosaicWait = mosaicH; \
1002 } \
1003 --mosaicWait; \
1004 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1005 ++pixel; \
1006 } \
1007 x = 0; \
1008 }
1009
1010#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
1011 for (; tileX < tileEnd; ++tileX) { \
1012 BACKGROUND_TEXT_SELECT_CHARACTER; \
1013 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
1014 palette = &mainPalette[paletteData]; \
1015 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
1016 if (UNLIKELY(charBase >= 0x10000)) { \
1017 pixel += 8; \
1018 continue; \
1019 } \
1020 LOAD_32(tileData, charBase, vram); \
1021 if (tileData) { \
1022 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1023 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1024 ++pixel; \
1025 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1026 ++pixel; \
1027 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1028 ++pixel; \
1029 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1030 ++pixel; \
1031 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1032 ++pixel; \
1033 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1034 ++pixel; \
1035 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1036 ++pixel; \
1037 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1038 ++pixel; \
1039 } else { \
1040 pixel += 7; \
1041 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1042 --pixel; \
1043 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1044 --pixel; \
1045 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1046 --pixel; \
1047 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1048 --pixel; \
1049 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1050 --pixel; \
1051 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1052 --pixel; \
1053 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1054 --pixel; \
1055 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1056 pixel += 8; \
1057 } \
1058 } else { \
1059 pixel += 8; \
1060 } \
1061 }
1062
1063#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
1064 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1065 int end2 = end - 4; \
1066 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1067 int shift = inX & 0x3; \
1068 if (LIKELY(charBase < 0x10000)) { \
1069 if (end2 > outX) { \
1070 LOAD_32(tileData, charBase, vram); \
1071 tileData >>= 8 * shift; \
1072 shift = 0; \
1073 for (; outX < end2; ++outX, ++pixel) { \
1074 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1075 } \
1076 } \
1077 } \
1078 \
1079 if (LIKELY(charBase < 0x10000)) { \
1080 LOAD_32(tileData, charBase + 4, vram); \
1081 tileData >>= 8 * shift; \
1082 for (; outX < end; ++outX, ++pixel) { \
1083 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1084 } \
1085 } \
1086 } else { \
1087 int start = outX; \
1088 outX = end - 1; \
1089 pixel = &renderer->row[outX]; \
1090 if (LIKELY(charBase < 0x10000)) { \
1091 if (end2 > start) { \
1092 LOAD_32(tileData, charBase, vram); \
1093 for (; outX >= end2; --outX, --pixel) { \
1094 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1095 } \
1096 charBase += 4; \
1097 } \
1098 } \
1099 \
1100 if (LIKELY(charBase < 0x10000)) { \
1101 LOAD_32(tileData, charBase, vram); \
1102 for (; outX >= renderer->start; --outX, --pixel) { \
1103 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1104 } \
1105 } \
1106 outX = end; \
1107 pixel = &renderer->row[outX]; \
1108 }
1109
1110#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
1111 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1112 if (UNLIKELY(charBase >= 0x10000)) { \
1113 return; \
1114 } \
1115 int end = mod8 - 4; \
1116 pixel = &renderer->row[outX]; \
1117 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1118 if (end > 0) { \
1119 LOAD_32(tileData, charBase, vram); \
1120 for (; outX < renderer->end - end; ++outX, ++pixel) { \
1121 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1122 } \
1123 charBase += 4; \
1124 } \
1125 \
1126 LOAD_32(tileData, charBase, vram); \
1127 for (; outX < renderer->end; ++outX, ++pixel) { \
1128 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1129 } \
1130 } else { \
1131 int shift = (8 - mod8) & 0x3; \
1132 int start = outX; \
1133 outX = renderer->end - 1; \
1134 pixel = &renderer->row[outX]; \
1135 if (end > 0) { \
1136 LOAD_32(tileData, charBase, vram); \
1137 tileData >>= 8 * shift; \
1138 for (; outX >= start + 4; --outX, --pixel) { \
1139 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1140 } \
1141 shift = 0; \
1142 } \
1143 \
1144 LOAD_32(tileData, charBase + 4, vram); \
1145 tileData >>= 8 * shift; \
1146 for (; outX >= start; --outX, --pixel) { \
1147 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1148 } \
1149 /* Needed for consistency checks */ \
1150 if (VIDEO_CHECKS) { \
1151 outX = renderer->end; \
1152 pixel = &renderer->row[outX]; \
1153 } \
1154 }
1155
1156#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
1157 for (; tileX < tileEnd; ++tileX) { \
1158 BACKGROUND_TEXT_SELECT_CHARACTER; \
1159 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1160 if (UNLIKELY(charBase >= 0x10000)) { \
1161 pixel += 8; \
1162 continue; \
1163 } \
1164 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1165 LOAD_32(tileData, charBase, vram); \
1166 if (tileData) { \
1167 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1168 ++pixel; \
1169 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1170 ++pixel; \
1171 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1172 ++pixel; \
1173 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1174 ++pixel; \
1175 } else { \
1176 pixel += 4; \
1177 } \
1178 LOAD_32(tileData, charBase + 4, vram); \
1179 if (tileData) { \
1180 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1181 ++pixel; \
1182 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1183 ++pixel; \
1184 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1185 ++pixel; \
1186 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1187 ++pixel; \
1188 } else { \
1189 pixel += 4; \
1190 } \
1191 } else { \
1192 LOAD_32(tileData, charBase + 4, vram); \
1193 if (tileData) { \
1194 pixel += 3; \
1195 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1196 --pixel; \
1197 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1198 --pixel; \
1199 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1200 --pixel; \
1201 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1202 } \
1203 pixel += 4; \
1204 LOAD_32(tileData, charBase, vram); \
1205 if (tileData) { \
1206 pixel += 3; \
1207 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1208 --pixel; \
1209 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1210 --pixel; \
1211 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1212 --pixel; \
1213 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1214 } \
1215 pixel += 4; \
1216 } \
1217 }
1218
1219#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
1220 for (; tileX < tileEnd; ++tileX) { \
1221 BACKGROUND_TEXT_SELECT_CHARACTER; \
1222 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1223 tileData = carryData; \
1224 for (x = 0; x < 8; ++x) { \
1225 if (!mosaicWait) { \
1226 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1227 if (x >= 4) { \
1228 LOAD_32(tileData, charBase + 4, vram); \
1229 tileData >>= (x - 4) * 8; \
1230 } else { \
1231 LOAD_32(tileData, charBase, vram); \
1232 tileData >>= x * 8; \
1233 } \
1234 } else { \
1235 if (x >= 4) { \
1236 LOAD_32(tileData, charBase, vram); \
1237 tileData >>= (7 - x) * 8; \
1238 } else { \
1239 LOAD_32(tileData, charBase + 4, vram); \
1240 tileData >>= (3 - x) * 8; \
1241 } \
1242 } \
1243 tileData &= 0xFF; \
1244 carryData = tileData; \
1245 mosaicWait = mosaicH; \
1246 } \
1247 tileData |= tileData << 8; \
1248 --mosaicWait; \
1249 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1250 ++pixel; \
1251 } \
1252 }
1253
1254#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
1255 uint32_t* pixel = &renderer->row[outX]; \
1256 if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
1257 int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
1258 int x; \
1259 int mosaicWait = (mosaicH - outX + VIDEO_HORIZONTAL_PIXELS * mosaicH) % mosaicH; \
1260 int carryData = 0; \
1261 paletteData = 0; /* Quiets compiler warning */ \
1262 DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
1263 return; \
1264 } \
1265 \
1266 if (inX & 0x7) { \
1267 BACKGROUND_TEXT_SELECT_CHARACTER; \
1268 \
1269 int mod8 = inX & 0x7; \
1270 int end = outX + 0x8 - mod8; \
1271 if (end > renderer->end) { \
1272 end = renderer->end; \
1273 } \
1274 if (UNLIKELY(end == outX)) { \
1275 return; \
1276 } \
1277 if (UNLIKELY(end < outX)) { \
1278 GBALog(0, GBA_LOG_DANGER, "Out of bounds background draw!"); \
1279 return; \
1280 } \
1281 DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
1282 outX = end; \
1283 if (tileX < tileEnd) { \
1284 ++tileX; \
1285 } else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
1286 GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
1287 return; \
1288 } \
1289 length -= end - renderer->start; \
1290 } \
1291 /*! TODO: Make sure these lines can be removed */ \
1292 /*!*/ pixel = &renderer->row[outX]; \
1293 outX += (tileEnd - tileX) * 8; \
1294 /*!*/ if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
1295 /*!*/ GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw would occur!"); \
1296 /*!*/ return; \
1297 /*!*/ } \
1298 DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
1299 if (length & 0x7) { \
1300 BACKGROUND_TEXT_SELECT_CHARACTER; \
1301 \
1302 int mod8 = length & 0x7; \
1303 if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
1304 GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw!"); \
1305 return; \
1306 } \
1307 DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
1308 } \
1309 if (VIDEO_CHECKS && UNLIKELY(&renderer->row[outX] != pixel)) { \
1310 GBALog(0, GBA_LOG_FATAL, "Background draw ended in the wrong place! Diff: %" PRIXPTR, &renderer->row[outX] - pixel); \
1311 } \
1312 if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
1313 GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw occurred!"); \
1314 return; \
1315 }
1316
1317static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
1318 int inX = renderer->start + background->x;
1319 int length = renderer->end - renderer->start;
1320 if (background->mosaic) {
1321 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
1322 y -= y % mosaicV;
1323 }
1324 int inY = y + background->y;
1325 uint16_t mapData;
1326
1327 unsigned yBase = inY & 0xF8;
1328 if (background->size == 2) {
1329 yBase += inY & 0x100;
1330 } else if (background->size == 3) {
1331 yBase += (inY & 0x100) << 1;
1332 }
1333 yBase = (background->screenBase >> 1) + (yBase << 2);
1334
1335 int localX;
1336 int localY;
1337
1338 unsigned xBase;
1339
1340 int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
1341 flags |= FLAG_TARGET_2 * background->target2;
1342 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed));
1343 objwinFlags |= flags;
1344 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
1345 if (renderer->blda == 0x10 && renderer->bldb == 0) {
1346 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
1347 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
1348 }
1349
1350 uint32_t screenBase;
1351 uint32_t charBase;
1352 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1353 color_t* mainPalette = renderer->normalPalette;
1354 if (variant) {
1355 mainPalette = renderer->variantPalette;
1356 }
1357 color_t* palette = mainPalette;
1358 PREPARE_OBJWIN;
1359
1360 int outX = renderer->start;
1361
1362 uint32_t tileData;
1363 uint32_t current;
1364 int pixelData;
1365 int paletteData;
1366 int tileX = 0;
1367 int tileEnd = ((length + inX) >> 3) - (inX >> 3);
1368 uint16_t* vram = renderer->d.vram;
1369
1370 if (!objwinSlowPath) {
1371 if (!(flags & FLAG_TARGET_2) && renderer->blendEffect != BLEND_ALPHA) {
1372 if (!background->multipalette) {
1373 DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
1374 } else {
1375 DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
1376 }
1377 } else {
1378 if (!background->multipalette) {
1379 DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
1380 } else {
1381 DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
1382 }
1383 }
1384 } else {
1385 if (!(flags & FLAG_TARGET_2) && renderer->blendEffect != BLEND_ALPHA) {
1386 if (!background->multipalette) {
1387 DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
1388 } else {
1389 DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
1390 }
1391 } else {
1392 if (!background->multipalette) {
1393 DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
1394 } else {
1395 DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
1396 }
1397 }
1398 }
1399}
1400
1401#define BACKGROUND_BITMAP_INIT \
1402 int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1403 int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1404 int mosaicH = 0; \
1405 int mosaicWait = 0; \
1406 if (background->mosaic) { \
1407 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
1408 y -= (inY % mosaicV) * background->dmy; \
1409 x -= (inY % mosaicV) * background->dmx; \
1410 mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \
1411 mosaicWait = renderer->start % (mosaicH + 1); \
1412 } \
1413 int32_t localX; \
1414 int32_t localY; \
1415 \
1416 int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
1417 flags |= FLAG_TARGET_2 * background->target2; \
1418 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed)); \
1419 objwinFlags |= flags; \
1420 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \
1421 if (renderer->blda == 0x10 && renderer->bldb == 0) { \
1422 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
1423 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
1424 } \
1425 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1426 color_t* palette = renderer->normalPalette; \
1427 if (variant) { \
1428 palette = renderer->variantPalette; \
1429 } \
1430 UNUSED(palette); \
1431 PREPARE_OBJWIN;
1432
1433#define BACKGROUND_BITMAP_ITERATE(W, H) \
1434 x += background->dx; \
1435 y += background->dy; \
1436 \
1437 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1438 continue; \
1439 } else { \
1440 localX = x; \
1441 localY = y; \
1442 }
1443
1444static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1445 int sizeAdjusted = 0x8000 << background->size;
1446
1447 BACKGROUND_BITMAP_INIT;
1448
1449 uint32_t screenBase = background->screenBase;
1450 uint32_t charBase = background->charBase;
1451 uint8_t mapData;
1452 uint8_t tileData = 0;
1453
1454 int outX;
1455 uint32_t* pixel;
1456 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1457 x += background->dx;
1458 y += background->dy;
1459
1460 if (!mosaicWait) {
1461 if (background->overflow) {
1462 localX = x & (sizeAdjusted - 1);
1463 localY = y & (sizeAdjusted - 1);
1464 } else if ((x | y) & ~(sizeAdjusted - 1)) {
1465 continue;
1466 } else {
1467 localX = x;
1468 localY = y;
1469 }
1470 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1471 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1472
1473 mosaicWait = mosaicH;
1474 } else {
1475 --mosaicWait;
1476 }
1477
1478 uint32_t current = *pixel;
1479 if (tileData && IS_WRITABLE(current)) {
1480 if (!objwinSlowPath) {
1481 _compositeBlendNoObjwin(renderer, pixel, palette[tileData] | flags, current);
1482 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1483 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1484 unsigned mergedFlags = flags;
1485 if (current & FLAG_OBJWIN) {
1486 mergedFlags = objwinFlags;
1487 }
1488 _compositeBlendObjwin(renderer, pixel, currentPalette[tileData] | mergedFlags, current);
1489 }
1490 }
1491 }
1492}
1493
1494static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1495 BACKGROUND_BITMAP_INIT;
1496
1497 uint32_t color = renderer->normalPalette[0];
1498
1499 int outX;
1500 uint32_t* pixel;
1501 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1502 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1503
1504 if (!mosaicWait) {
1505 LOAD_16(color, ((localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
1506#ifndef COLOR_16_BIT
1507 unsigned color32;
1508 color32 = 0;
1509 color32 |= (color << 3) & 0xF8;
1510 color32 |= (color << 6) & 0xF800;
1511 color32 |= (color << 9) & 0xF80000;
1512 color = color32;
1513#endif
1514 mosaicWait = mosaicH;
1515 } else {
1516 --mosaicWait;
1517 }
1518
1519 uint32_t current = *pixel;
1520 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1521 unsigned mergedFlags = flags;
1522 if (current & FLAG_OBJWIN) {
1523 mergedFlags = objwinFlags;
1524 }
1525 if (!variant) {
1526 _compositeBlendObjwin(renderer, pixel, color | mergedFlags, current);
1527 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1528 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | mergedFlags, current);
1529 } else if (renderer->blendEffect == BLEND_DARKEN) {
1530 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | mergedFlags, current);
1531 }
1532 }
1533 }
1534}
1535
1536static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1537 BACKGROUND_BITMAP_INIT;
1538
1539 uint16_t color = renderer->normalPalette[0];
1540 uint32_t offset = 0;
1541 if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1542 offset = 0xA000;
1543 }
1544
1545 int outX;
1546 uint32_t* pixel;
1547 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1548 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1549
1550 if (!mosaicWait) {
1551 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1552
1553 mosaicWait = mosaicH;
1554 } else {
1555 --mosaicWait;
1556 }
1557
1558 uint32_t current = *pixel;
1559 if (color && IS_WRITABLE(current)) {
1560 if (!objwinSlowPath) {
1561 _compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
1562 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1563 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1564 unsigned mergedFlags = flags;
1565 if (current & FLAG_OBJWIN) {
1566 mergedFlags = objwinFlags;
1567 }
1568 _compositeBlendObjwin(renderer, pixel, currentPalette[color] | mergedFlags, current);
1569 }
1570 }
1571 }
1572}
1573
1574static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1575 BACKGROUND_BITMAP_INIT;
1576
1577 uint32_t color = renderer->normalPalette[0];
1578 uint32_t offset = 0;
1579 if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1580 offset = 0xA000;
1581 }
1582
1583 int outX;
1584 uint32_t* pixel;
1585 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1586 BACKGROUND_BITMAP_ITERATE(160, 128);
1587
1588 if (!mosaicWait) {
1589 LOAD_16(color, (offset + (localX >> 8) + (localY >> 8) * 160) << 1, renderer->d.vram);
1590#ifndef COLOR_16_BIT
1591 unsigned color32 = 0;
1592 color32 |= (color << 9) & 0xF80000;
1593 color32 |= (color << 3) & 0xF8;
1594 color32 |= (color << 6) & 0xF800;
1595 color = color32;
1596#endif
1597 mosaicWait = mosaicH;
1598 } else {
1599 --mosaicWait;
1600 }
1601
1602 uint32_t current = *pixel;
1603 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1604 unsigned mergedFlags = flags;
1605 if (current & FLAG_OBJWIN) {
1606 mergedFlags = objwinFlags;
1607 }
1608 if (!variant) {
1609 _compositeBlendObjwin(renderer, pixel, color | mergedFlags, current);
1610 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1611 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | mergedFlags, current);
1612 } else if (renderer->blendEffect == BLEND_DARKEN) {
1613 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | mergedFlags, current);
1614 }
1615 }
1616 }
1617}
1618
1619#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1620 SPRITE_YBASE_ ## DEPTH(inY); \
1621 unsigned tileData; \
1622 for (; outX < condition; ++outX, inX += xOffset) { \
1623 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1624 continue; \
1625 } \
1626 SPRITE_XBASE_ ## DEPTH(inX); \
1627 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1628 }
1629
1630#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1631 SPRITE_YBASE_ ## DEPTH(inY); \
1632 unsigned tileData; \
1633 if (outX % mosaicH) { \
1634 if (!inX && xOffset > 0) { \
1635 inX = mosaicH - (outX % mosaicH); \
1636 outX += mosaicH - (outX % mosaicH); \
1637 } else if (inX == width - xOffset) { \
1638 inX = mosaicH + (outX % mosaicH); \
1639 outX += mosaicH - (outX % mosaicH); \
1640 } \
1641 } \
1642 for (; outX < condition; ++outX, inX += xOffset) { \
1643 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1644 continue; \
1645 } \
1646 int localX = inX - xOffset * (outX % mosaicH); \
1647 if (localX < 0 || localX > width - 1) { \
1648 continue; \
1649 } \
1650 SPRITE_XBASE_ ## DEPTH(localX); \
1651 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1652 }
1653
1654#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1655 unsigned tileData; \
1656 for (; outX < x + totalWidth && outX < end; ++outX, ++inX) { \
1657 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1658 continue; \
1659 } \
1660 xAccum += mat.a; \
1661 yAccum += mat.c; \
1662 int localX = (xAccum >> 8) + (width >> 1); \
1663 int localY = (yAccum >> 8) + (height >> 1); \
1664 \
1665 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1666 continue; \
1667 } \
1668 \
1669 SPRITE_YBASE_ ## DEPTH(localY); \
1670 SPRITE_XBASE_ ## DEPTH(localX); \
1671 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1672 }
1673
1674#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1675#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1676
1677#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1678 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1679 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1680 current = renderer->spriteLayer[outX]; \
1681 if ((current & FLAG_ORDER_MASK) > flags) { \
1682 if (tileData) { \
1683 renderer->spriteLayer[outX] = palette[tileData] | flags; \
1684 } else if (current != FLAG_UNWRITTEN) { \
1685 renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
1686 } \
1687 }
1688
1689#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1690 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1691 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1692 if (tileData) { \
1693 renderer->row[outX] |= FLAG_OBJWIN; \
1694 }
1695
1696#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1697#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width : 0x80) + (localY & 0x7) * 8;
1698
1699#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1700 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1701 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1702 current = renderer->spriteLayer[outX]; \
1703 if ((current & FLAG_ORDER_MASK) > flags) { \
1704 if (tileData) { \
1705 renderer->spriteLayer[outX] = palette[tileData] | flags; \
1706 } else if (current != FLAG_UNWRITTEN) { \
1707 renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
1708 } \
1709 }
1710
1711#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1712 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1713 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1714 if (tileData) { \
1715 renderer->row[outX] |= FLAG_OBJWIN; \
1716 }
1717
1718static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1719 int width = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2];
1720 int height = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2 + 1];
1721 int start = renderer->start;
1722 int end = renderer->end;
1723 uint32_t flags = GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY;
1724 flags |= FLAG_TARGET_1 * ((GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
1725 flags |= FLAG_OBJWIN * (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN);
1726 int32_t x = GBAObjAttributesBGetX(sprite->b) << 23;
1727 x >>= 23;
1728 uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
1729 unsigned charBase = GBAObjAttributesCGetTile(sprite->c) * 0x20;
1730 int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1731 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1732 // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1733 variant = 0;
1734 }
1735 color_t* palette = &renderer->normalPalette[0x100];
1736 if (variant) {
1737 palette = &renderer->variantPalette[0x100];
1738 }
1739
1740 int inY = y - (int) GBAObjAttributesAGetY(sprite->a);
1741
1742 uint32_t current;
1743 if (GBAObjAttributesAIsTransformed(sprite->a)) {
1744 int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a);
1745 int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a);
1746 struct GBAOAMMatrix mat;
1747 LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
1748 LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
1749 LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
1750 LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
1751
1752 if (inY < 0) {
1753 inY += 256;
1754 }
1755 int outX = x >= start ? x : start;
1756 int inX = outX - x;
1757 int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1));
1758 int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1));
1759
1760 if (!GBAObjAttributesAIs256Color(sprite->a)) {
1761 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1762 if (flags & FLAG_OBJWIN) {
1763 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1764 } else {
1765 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1766 }
1767 } else {
1768 if (flags & FLAG_OBJWIN) {
1769 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1770 } else {
1771 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1772 }
1773 }
1774 } else {
1775 int outX = x >= start ? x : start;
1776 int condition = x + width;
1777 int mosaicH = 1;
1778 if (GBAObjAttributesAIsMosaic(sprite->a)) {
1779 mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
1780 if (condition % mosaicH) {
1781 condition += mosaicH - (condition % mosaicH);
1782 }
1783 }
1784 if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) {
1785 inY += 256;
1786 }
1787 if (GBAObjAttributesBIsVFlip(sprite->b)) {
1788 inY = height - inY - 1;
1789 }
1790 if (end < condition) {
1791 condition = end;
1792 }
1793 int inX = outX - x;
1794 int xOffset = 1;
1795 if (GBAObjAttributesBIsHFlip(sprite->b)) {
1796 inX = width - inX - 1;
1797 xOffset = -1;
1798 }
1799 if (!GBAObjAttributesAIs256Color(sprite->a)) {
1800 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1801 if (flags & FLAG_OBJWIN) {
1802 SPRITE_NORMAL_LOOP(16, OBJWIN);
1803 } else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1804 SPRITE_MOSAIC_LOOP(16, NORMAL);
1805 } else {
1806 SPRITE_NORMAL_LOOP(16, NORMAL);
1807 }
1808 } else {
1809 if (flags & FLAG_OBJWIN) {
1810 SPRITE_NORMAL_LOOP(256, OBJWIN);
1811 } else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1812 SPRITE_MOSAIC_LOOP(256, NORMAL);
1813 } else {
1814 SPRITE_NORMAL_LOOP(256, NORMAL);
1815 }
1816 }
1817 }
1818 return 1;
1819}
1820
1821static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1822 int x;
1823 uint32_t* pixel = &renderer->row[renderer->start];
1824 uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
1825
1826 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt);
1827 bool objwinDisable = false;
1828 bool objwinOnly = false;
1829 if (objwinSlowPath) {
1830 objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed);
1831 objwinOnly = !objwinDisable && !GBAWindowControlIsObjEnable(renderer->currentWindow.packed);
1832
1833 if (objwinDisable) {
1834 for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1835 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1836 uint32_t current = *pixel;
1837 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1838 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1839 }
1840 }
1841 return;
1842 } else if (objwinOnly) {
1843 for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1844 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1845 uint32_t current = *pixel;
1846 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1847 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1848 }
1849 }
1850 return;
1851 } else {
1852 for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1853 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1854 uint32_t current = *pixel;
1855 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1856 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1857 }
1858 }
1859 return;
1860 }
1861 }
1862 for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1863 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1864 uint32_t current = *pixel;
1865 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1866 _compositeBlendNoObjwin(renderer, pixel, color | flags, current);
1867 }
1868 }
1869}
1870
1871static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1872 int i;
1873 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1874 for (i = 0; i < 512; ++i) {
1875 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1876 }
1877 } else if (renderer->blendEffect == BLEND_DARKEN) {
1878 for (i = 0; i < 512; ++i) {
1879 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1880 }
1881 } else {
1882 for (i = 0; i < 512; ++i) {
1883 renderer->variantPalette[i] = renderer->normalPalette[i];
1884 }
1885 }
1886}
1887
1888static inline unsigned _brighten(unsigned color, int y) {
1889 unsigned c = 0;
1890 unsigned a;
1891#ifdef COLOR_16_BIT
1892 a = color & 0x1F;
1893 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1894
1895#ifdef COLOR_5_6_5
1896 a = color & 0x7C0;
1897 c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1898
1899 a = color & 0xF800;
1900 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1901#else
1902 a = color & 0x3E0;
1903 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1904
1905 a = color & 0x7C00;
1906 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1907#endif
1908#else
1909 a = color & 0xF8;
1910 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1911
1912 a = color & 0xF800;
1913 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1914
1915 a = color & 0xF80000;
1916 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1917#endif
1918 return c;
1919}
1920
1921static inline unsigned _darken(unsigned color, int y) {
1922 unsigned c = 0;
1923 unsigned a;
1924#ifdef COLOR_16_BIT
1925 a = color & 0x1F;
1926 c |= (a - (a * y) / 16) & 0x1F;
1927
1928#ifdef COLOR_5_6_5
1929 a = color & 0x7C0;
1930 c |= (a - (a * y) / 16) & 0x7C0;
1931
1932 a = color & 0xF800;
1933 c |= (a - (a * y) / 16) & 0xF800;
1934#else
1935 a = color & 0x3E0;
1936 c |= (a - (a * y) / 16) & 0x3E0;
1937
1938 a = color & 0x7C00;
1939 c |= (a - (a * y) / 16) & 0x7C00;
1940#endif
1941#else
1942 a = color & 0xF8;
1943 c |= (a - (a * y) / 16) & 0xF8;
1944
1945 a = color & 0xF800;
1946 c |= (a - (a * y) / 16) & 0xF800;
1947
1948 a = color & 0xF80000;
1949 c |= (a - (a * y) / 16) & 0xF80000;
1950#endif
1951 return c;
1952}
1953
1954static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1955 unsigned c = 0;
1956 unsigned a, b;
1957#ifdef COLOR_16_BIT
1958#ifdef COLOR_5_6_5
1959 a = colorA & 0xF81F;
1960 b = colorB & 0xF81F;
1961 a |= (colorA & 0x7C0) << 16;
1962 b |= (colorB & 0x7C0) << 16;
1963 c = ((a * weightA + b * weightB) / 16);
1964 if (c & 0x08000000) {
1965 c = (c & ~0x0FC00000) | 0x07C00000;
1966 }
1967 if (c & 0x0020) {
1968 c = (c & ~0x003F) | 0x001F;
1969 }
1970 if (c & 0x10000) {
1971 c = (c & ~0x1F800) | 0xF800;
1972 }
1973 c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
1974#else
1975 a = colorA & 0x7C1F;
1976 b = colorB & 0x7C1F;
1977 a |= (colorA & 0x3E0) << 16;
1978 b |= (colorB & 0x3E0) << 16;
1979 c = ((a * weightA + b * weightB) / 16);
1980 if (c & 0x04000000) {
1981 c = (c & ~0x07E00000) | 0x03E00000;
1982 }
1983 if (c & 0x0020) {
1984 c = (c & ~0x003F) | 0x001F;
1985 }
1986 if (c & 0x10000) {
1987 c = (c & ~0x1F800) | 0xF800;
1988 }
1989 c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
1990#endif
1991#else
1992 a = colorA & 0xF8;
1993 b = colorB & 0xF8;
1994 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1995 if (c & 0x00000100) {
1996 c = 0x000000F8;
1997 }
1998
1999 a = colorA & 0xF800;
2000 b = colorB & 0xF800;
2001 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
2002 if (c & 0x00010000) {
2003 c = (c & 0x000000F8) | 0x0000F800;
2004 }
2005
2006 a = colorA & 0xF80000;
2007 b = colorB & 0xF80000;
2008 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
2009 if (c & 0x01000000) {
2010 c = (c & 0x0000F8F8) | 0x00F80000;
2011 }
2012#endif
2013 return c;
2014}