all repos — mgba @ f51044e94c9616868526802a71ffca1ec12a1ca1

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