all repos — mgba @ 618092a5f6b5ed1501e361b4c7c2f390f4967d66

mGBA Game Boy Advance Emulator

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}