all repos — mgba @ ec5445d5ada31006cf154f5cba767b4239b7ffd3

mGBA Game Boy Advance Emulator

src/gba/renderers/video-software.c (view raw)

   1/* Copyright (c) 2013-2014 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.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		_composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
 811	}
 812
 813#define COMPOSITE_16_NO_OBJWIN(BLEND) \
 814	_composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
 815
 816#define COMPOSITE_256_OBJWIN(BLEND) \
 817	if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
 818		unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
 819		_composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
 820	}
 821
 822#define COMPOSITE_256_NO_OBJWIN(BLEND) \
 823	COMPOSITE_16_NO_OBJWIN(BLEND)
 824
 825#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \
 826	pixelData = tileData & 0xF; \
 827	current = *pixel; \
 828	if (pixelData && IS_WRITABLE(current)) { \
 829		COMPOSITE_16_ ## OBJWIN (BLEND); \
 830	} \
 831	tileData >>= 4;
 832
 833#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN) \
 834	pixelData = tileData & 0xFF; \
 835	current = *pixel; \
 836	if (pixelData && IS_WRITABLE(current)) { \
 837		COMPOSITE_256_ ## OBJWIN (BLEND); \
 838	} \
 839	tileData >>= 8;
 840
 841#define BACKGROUND_TEXT_SELECT_CHARACTER \
 842	localX = tileX * 8 + inX; \
 843	xBase = localX & 0xF8; \
 844	if (background->size & 1) { \
 845		xBase += (localX & 0x100) << 5; \
 846	} \
 847	screenBase = yBase + (xBase >> 3); \
 848	LOAD_16(mapData, screenBase << 1, vram); \
 849	localY = inY & 0x7; \
 850	if (GBA_TEXT_MAP_VFLIP(mapData)) { \
 851		localY = 7 - localY; \
 852	}
 853
 854#define PREPARE_OBJWIN \
 855	int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \
 856	int objwinOnly = 0; \
 857	int objwinForceEnable = 0; \
 858	color_t* objwinPalette; \
 859	if (objwinSlowPath) { \
 860		if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
 861			objwinPalette = renderer->variantPalette; \
 862		} else { \
 863			objwinPalette = renderer->normalPalette; \
 864		} \
 865		switch (background->index) { \
 866		case 0: \
 867			objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
 868			objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
 869			break; \
 870		case 1: \
 871			objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
 872			objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
 873			break; \
 874		case 2: \
 875			objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
 876			objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
 877			break; \
 878		case 3: \
 879			objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
 880			objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
 881			break; \
 882		} \
 883	}
 884
 885#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
 886	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 887	palette = &mainPalette[paletteData]; \
 888	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 889	LOAD_32(tileData, charBase, vram); \
 890	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 891		tileData >>= 4 * mod8; \
 892		for (; outX < end; ++outX, ++pixel) { \
 893			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 894		} \
 895	} else { \
 896		for (outX = end - 1; outX >= renderer->start; --outX) { \
 897			uint32_t* pixel = &renderer->row[outX]; \
 898			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 899		} \
 900	}
 901
 902#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
 903	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 904	LOAD_32(tileData, charBase, vram); \
 905	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 906	palette = &mainPalette[paletteData]; \
 907	pixel = &renderer->row[outX]; \
 908	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 909		if (outX < renderer->start) { \
 910			tileData >>= 4 * (renderer->start - outX); \
 911			outX = renderer->start; \
 912			pixel = &renderer->row[outX]; \
 913		} \
 914		for (; outX < renderer->end; ++outX, ++pixel) { \
 915			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 916		} \
 917	} else { \
 918		tileData >>= 4 * (0x8 - mod8); \
 919		int end = renderer->end - 8; \
 920		if (end < -1) { \
 921			end = -1; \
 922		} \
 923		outX = renderer->end - 1; \
 924		pixel = &renderer->row[outX]; \
 925		for (; outX > end; --outX, --pixel) { \
 926			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 927		} \
 928		/* Needed for consistency checks */ \
 929		if (VIDEO_CHECKS) { \
 930			outX = renderer->end; \
 931			pixel = &renderer->row[outX]; \
 932		} \
 933	}
 934
 935#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
 936	x = inX & 7; \
 937	if (mosaicWait) { \
 938		int baseX = x - (mosaicH - mosaicWait); \
 939		if (baseX < 0) { \
 940			int disturbX = (16 + baseX) >> 3; \
 941			inX -= disturbX << 3; \
 942			BACKGROUND_TEXT_SELECT_CHARACTER; \
 943			baseX -= disturbX << 3; \
 944			inX += disturbX << 3; \
 945		} else { \
 946			BACKGROUND_TEXT_SELECT_CHARACTER; \
 947		} \
 948		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 949		paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 950		palette = &mainPalette[paletteData]; \
 951		LOAD_32(tileData, charBase, vram); \
 952		if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 953			tileData >>= 4 * baseX; \
 954		} else { \
 955			tileData >>= 4 * (7 - baseX); \
 956		} \
 957		tileData &= 0xF; \
 958		tileData |= tileData << 4; \
 959		tileData |= tileData << 8; \
 960		tileData |= tileData << 12; \
 961		tileData |= tileData << 16; \
 962		tileData |= tileData << 20; \
 963		tileData |= tileData << 24; \
 964		tileData |= tileData << 28; \
 965		carryData = tileData; \
 966	} \
 967	for (; length; ++tileX) { \
 968		BACKGROUND_TEXT_SELECT_CHARACTER; \
 969		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 970		tileData = carryData; \
 971		for (; x < 8 && length; ++x, --length) { \
 972			if (!mosaicWait) { \
 973				paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 974				palette = &mainPalette[paletteData]; \
 975				LOAD_32(tileData, charBase, vram); \
 976				if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 977					tileData >>= x * 4; \
 978				} else { \
 979					tileData >>= (7 - x) * 4; \
 980				} \
 981				tileData &= 0xF; \
 982				tileData |= tileData << 4; \
 983				tileData |= tileData << 8; \
 984				tileData |= tileData << 12; \
 985				tileData |= tileData << 16; \
 986				tileData |= tileData << 20; \
 987				tileData |= tileData << 24; \
 988				tileData |= tileData << 28; \
 989				carryData = tileData; \
 990				mosaicWait = mosaicH; \
 991			} \
 992			--mosaicWait; \
 993			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 994			++pixel; \
 995		} \
 996		x = 0; \
 997	}
 998
 999#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
1000	for (; tileX < tileEnd; ++tileX) { \
1001		BACKGROUND_TEXT_SELECT_CHARACTER; \
1002		paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
1003		palette = &mainPalette[paletteData]; \
1004		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
1005		LOAD_32(tileData, charBase, vram); \
1006		if (tileData) { \
1007			if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1008				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1009				++pixel; \
1010				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1011				++pixel; \
1012				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1013				++pixel; \
1014				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1015				++pixel; \
1016				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1017				++pixel; \
1018				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1019				++pixel; \
1020				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1021				++pixel; \
1022				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1023				++pixel; \
1024			} else { \
1025				pixel += 7; \
1026				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1027				--pixel; \
1028				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1029				--pixel; \
1030				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1031				--pixel; \
1032				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1033				--pixel; \
1034				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1035				--pixel; \
1036				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1037				--pixel; \
1038				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1039				--pixel; \
1040				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1041				pixel += 8; \
1042			} \
1043		} else { \
1044			pixel += 8; \
1045		} \
1046	}
1047
1048#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
1049	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1050	int end2 = end - 4; \
1051	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1052		int shift = inX & 0x3; \
1053		if (end2 > outX) { \
1054			LOAD_32(tileData, charBase, vram); \
1055			tileData >>= 8 * shift; \
1056			shift = 0; \
1057			for (; outX < end2; ++outX, ++pixel) { \
1058				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1059			} \
1060		} \
1061		\
1062		LOAD_32(tileData, charBase + 4, vram); \
1063		tileData >>= 8 * shift; \
1064		for (; outX < end; ++outX, ++pixel) { \
1065			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1066		} \
1067	} else { \
1068		int start = outX; \
1069		outX = end - 1; \
1070		pixel = &renderer->row[outX]; \
1071		if (end2 > start) { \
1072			LOAD_32(tileData, charBase, vram); \
1073			for (; outX >= end2; --outX, --pixel) { \
1074				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1075			} \
1076			charBase += 4; \
1077		} \
1078		\
1079		LOAD_32(tileData, charBase, vram); \
1080		for (; outX >= renderer->start; --outX, --pixel) { \
1081			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1082		} \
1083		outX = end; \
1084		pixel = &renderer->row[outX]; \
1085	}
1086
1087#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
1088	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1089	int end = mod8 - 4; \
1090	pixel = &renderer->row[outX]; \
1091	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1092		if (end > 0) { \
1093			LOAD_32(tileData, charBase, vram); \
1094			for (; outX < renderer->end - end; ++outX, ++pixel) { \
1095				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1096			} \
1097			charBase += 4; \
1098		} \
1099		\
1100		LOAD_32(tileData, charBase, vram); \
1101		for (; outX < renderer->end; ++outX, ++pixel) { \
1102			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1103		} \
1104	} else { \
1105		int shift = (8 - mod8) & 0x3; \
1106		int start = outX; \
1107		outX = renderer->end - 1; \
1108		pixel = &renderer->row[outX]; \
1109		if (end > 0) { \
1110			LOAD_32(tileData, charBase, vram); \
1111			tileData >>= 8 * shift; \
1112			for (; outX >= start + 4; --outX, --pixel) { \
1113				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1114			} \
1115			shift = 0; \
1116		} \
1117		\
1118		LOAD_32(tileData, charBase + 4, vram); \
1119		tileData >>= 8 * shift; \
1120		for (; outX >= start; --outX, --pixel) { \
1121			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1122		} \
1123		/* Needed for consistency checks */ \
1124		if (VIDEO_CHECKS) { \
1125			outX = renderer->end; \
1126			pixel = &renderer->row[outX]; \
1127		} \
1128	}
1129
1130#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
1131	for (; tileX < tileEnd; ++tileX) { \
1132		BACKGROUND_TEXT_SELECT_CHARACTER; \
1133		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1134		if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1135			LOAD_32(tileData, charBase, vram); \
1136			if (tileData) { \
1137					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1138					++pixel; \
1139					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1140					++pixel; \
1141					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1142					++pixel; \
1143					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1144					++pixel; \
1145			} else { \
1146				pixel += 4; \
1147			} \
1148			LOAD_32(tileData, charBase + 4, vram); \
1149			if (tileData) { \
1150					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1151					++pixel; \
1152					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1153					++pixel; \
1154					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1155					++pixel; \
1156					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1157					++pixel; \
1158			} else { \
1159				pixel += 4; \
1160			} \
1161		} else { \
1162			LOAD_32(tileData, charBase + 4, vram); \
1163			if (tileData) { \
1164				pixel += 3; \
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			} \
1173			pixel += 4; \
1174			LOAD_32(tileData, charBase, vram); \
1175			if (tileData) { \
1176				pixel += 3; \
1177				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1178				--pixel; \
1179				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1180				--pixel; \
1181				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1182				--pixel; \
1183				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1184			} \
1185			pixel += 4; \
1186		} \
1187	}
1188
1189#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
1190	for (; tileX < tileEnd; ++tileX) { \
1191		BACKGROUND_TEXT_SELECT_CHARACTER; \
1192		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1193		tileData = carryData; \
1194		for (x = 0; x < 8; ++x) { \
1195			if (!mosaicWait) { \
1196				if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1197					if (x >= 4) { \
1198						LOAD_32(tileData, charBase + 4, vram); \
1199						tileData >>= (x - 4) * 8; \
1200					} else { \
1201						LOAD_32(tileData, charBase, vram); \
1202						tileData >>= x * 8; \
1203					} \
1204				} else { \
1205					if (x >= 4) { \
1206						LOAD_32(tileData, charBase, vram); \
1207						tileData >>= (7 - x) * 8; \
1208					} else { \
1209						LOAD_32(tileData, charBase + 4, vram); \
1210						tileData >>= (3 - x) * 8; \
1211					} \
1212				} \
1213				tileData &= 0xFF; \
1214				carryData = tileData; \
1215				mosaicWait = mosaicH; \
1216			} \
1217			tileData |= tileData << 8; \
1218			--mosaicWait; \
1219			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1220			++pixel; \
1221		} \
1222	}
1223
1224#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
1225	uint32_t* pixel = &renderer->row[outX]; \
1226	if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
1227		int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
1228		int x; \
1229		int mosaicWait = (mosaicH - outX + VIDEO_HORIZONTAL_PIXELS * mosaicH) % mosaicH; \
1230		int carryData = 0; \
1231		paletteData = 0; /* Quiets compiler warning */ \
1232		DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
1233		return; \
1234	} \
1235	\
1236	if (inX & 0x7) { \
1237		BACKGROUND_TEXT_SELECT_CHARACTER; \
1238		\
1239		int mod8 = inX & 0x7; \
1240		int end = outX + 0x8 - mod8; \
1241		if (end > renderer->end) { \
1242			end = renderer->end; \
1243		} \
1244		if (UNLIKELY(end == outX)) { \
1245			return; \
1246		} \
1247		if (UNLIKELY(end < outX)) { \
1248			GBALog(0, GBA_LOG_DANGER, "Out of bounds background draw!"); \
1249			return; \
1250		} \
1251		DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
1252		outX = end; \
1253		if (tileX < tileEnd) { \
1254			++tileX; \
1255		} else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
1256			GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
1257			return; \
1258		} \
1259		length -= end - renderer->start; \
1260	} \
1261	/*! TODO: Make sure these lines can be removed */ \
1262	/*!*/ pixel = &renderer->row[outX]; \
1263	outX += (tileEnd - tileX) * 8; \
1264	/*!*/ if (VIDEO_CHECKS &&  UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
1265	/*!*/	GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw would occur!"); \
1266	/*!*/	return; \
1267	/*!*/ } \
1268	DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
1269	if (length & 0x7) { \
1270		BACKGROUND_TEXT_SELECT_CHARACTER; \
1271		\
1272		int mod8 = length & 0x7; \
1273		if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
1274			GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw!"); \
1275			return; \
1276		} \
1277		DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
1278	} \
1279	if (VIDEO_CHECKS && UNLIKELY(&renderer->row[outX] != pixel)) { \
1280		GBALog(0, GBA_LOG_FATAL, "Background draw ended in the wrong place! Diff: %" PRIXPTR, &renderer->row[outX] - pixel); \
1281	} \
1282	if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
1283		GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw occurred!"); \
1284		return; \
1285	}
1286
1287static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
1288	int inX = renderer->start + background->x;
1289	int length = renderer->end - renderer->start;
1290	if (background->mosaic) {
1291		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
1292		y -= y % mosaicV;
1293	}
1294	int inY = y + background->y;
1295	uint16_t mapData;
1296
1297	unsigned yBase = inY & 0xF8;
1298	if (background->size == 2) {
1299		yBase += inY & 0x100;
1300	} else if (background->size == 3) {
1301		yBase += (inY & 0x100) << 1;
1302	}
1303	yBase = (background->screenBase >> 1) + (yBase << 2);
1304
1305	int localX;
1306	int localY;
1307
1308	unsigned xBase;
1309
1310	int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
1311	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
1312	flags |= FLAG_TARGET_2 * background->target2;
1313	if (renderer->blda == 0x10 && renderer->bldb == 0) {
1314		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
1315	}
1316
1317	uint32_t screenBase;
1318	uint32_t charBase;
1319	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1320	color_t* mainPalette = renderer->normalPalette;
1321	if (variant) {
1322		mainPalette = renderer->variantPalette;
1323	}
1324	color_t* palette = mainPalette;
1325	PREPARE_OBJWIN;
1326
1327	int outX = renderer->start;
1328
1329	uint32_t tileData;
1330	uint32_t current;
1331	int pixelData;
1332	int paletteData;
1333	int tileX = 0;
1334	int tileEnd = ((length + inX) >> 3) - (inX >> 3);
1335	uint16_t* vram = renderer->d.vram;
1336
1337	if (!objwinSlowPath) {
1338		if (!(flags & FLAG_TARGET_2) && renderer->blendEffect != BLEND_ALPHA) {
1339			if (!background->multipalette) {
1340				DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
1341			} else {
1342				DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
1343			}
1344		} else {
1345			if (!background->multipalette) {
1346				DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
1347			} else {
1348				DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
1349			}
1350		}
1351	} else {
1352		if (!(flags & FLAG_TARGET_2) && renderer->blendEffect != BLEND_ALPHA) {
1353			if (!background->multipalette) {
1354				DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
1355			} else {
1356				DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
1357			}
1358		} else {
1359			if (!background->multipalette) {
1360				DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
1361			} else {
1362				DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
1363			}
1364		}
1365	}
1366}
1367
1368#define BACKGROUND_BITMAP_INIT \
1369	int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1370	int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1371	int mosaicH = 0; \
1372	int mosaicWait = 0; \
1373	if (background->mosaic) { \
1374		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
1375		y -= (inY % mosaicV) * background->dmy; \
1376		x -= (inY % mosaicV) * background->dmx; \
1377		mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \
1378		mosaicWait = renderer->start % (mosaicH + 1); \
1379	} \
1380	int32_t localX; \
1381	int32_t localY; \
1382	\
1383	int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
1384	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \
1385	flags |= FLAG_TARGET_2 * background->target2; \
1386	if (renderer->blda == 0x10 && renderer->bldb == 0) { \
1387		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
1388	} \
1389	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1390	color_t* palette = renderer->normalPalette; \
1391	if (variant) { \
1392		palette = renderer->variantPalette; \
1393	} \
1394	PREPARE_OBJWIN;
1395
1396#define BACKGROUND_BITMAP_ITERATE(W, H) \
1397	x += background->dx; \
1398	y += background->dy; \
1399	\
1400	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1401		continue; \
1402	} else { \
1403		localX = x; \
1404		localY = y; \
1405	}
1406
1407static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1408	int sizeAdjusted = 0x8000 << background->size;
1409
1410	BACKGROUND_BITMAP_INIT;
1411
1412	uint32_t screenBase = background->screenBase;
1413	uint32_t charBase = background->charBase;
1414	uint8_t mapData;
1415	uint8_t tileData = 0;
1416
1417	int outX;
1418	uint32_t* pixel;
1419	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1420		x += background->dx;
1421		y += background->dy;
1422
1423		if (!mosaicWait) {
1424			if (background->overflow) {
1425				localX = x & (sizeAdjusted - 1);
1426				localY = y & (sizeAdjusted - 1);
1427			} else if ((x | y) & ~(sizeAdjusted - 1)) {
1428				continue;
1429			} else {
1430				localX = x;
1431				localY = y;
1432			}
1433			mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1434			tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1435
1436			mosaicWait = mosaicH;
1437		} else {
1438			--mosaicWait;
1439		}
1440
1441		uint32_t current = *pixel;
1442		if (tileData && IS_WRITABLE(current)) {
1443			if (!objwinSlowPath) {
1444				_compositeBlendNoObjwin(renderer, pixel, palette[tileData] | flags, current);
1445			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1446				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1447				_compositeBlendObjwin(renderer, pixel, currentPalette[tileData] | flags, current);
1448			}
1449		}
1450	}
1451}
1452
1453static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1454	BACKGROUND_BITMAP_INIT;
1455
1456	uint32_t color = renderer->normalPalette[0];
1457
1458	int outX;
1459	uint32_t* pixel;
1460	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1461		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1462
1463		if (!mosaicWait) {
1464			LOAD_16(color, ((localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
1465#ifndef COLOR_16_BIT
1466			unsigned color32;
1467			color32 = 0;
1468			color32 |= (color << 3) & 0xF8;
1469			color32 |= (color << 6) & 0xF800;
1470			color32 |= (color << 9) & 0xF80000;
1471			color = color32;
1472#endif
1473			mosaicWait = mosaicH;
1474		} else {
1475			--mosaicWait;
1476		}
1477
1478		uint32_t current = *pixel;
1479		if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1480			if (!variant) {
1481				_compositeBlendObjwin(renderer, pixel, color | flags, current);
1482			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1483				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1484			} else if (renderer->blendEffect == BLEND_DARKEN) {
1485				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1486			}
1487		}
1488	}
1489}
1490
1491static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1492	BACKGROUND_BITMAP_INIT;
1493
1494	uint16_t color = renderer->normalPalette[0];
1495	uint32_t offset = 0;
1496	if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1497		offset = 0xA000;
1498	}
1499
1500	int outX;
1501	uint32_t* pixel;
1502	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1503		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1504
1505		if (!mosaicWait) {
1506			color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1507
1508			mosaicWait = mosaicH;
1509		} else {
1510			--mosaicWait;
1511		}
1512
1513		uint32_t current = *pixel;
1514		if (color && IS_WRITABLE(current)) {
1515			if (!objwinSlowPath) {
1516				_compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
1517			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1518				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1519				_compositeBlendObjwin(renderer, pixel, currentPalette[color] | flags, current);
1520			}
1521		}
1522	}
1523}
1524
1525static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1526	BACKGROUND_BITMAP_INIT;
1527
1528	uint32_t color = renderer->normalPalette[0];
1529	uint32_t offset = 0;
1530	if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1531		offset = 0xA000;
1532	}
1533
1534	int outX;
1535	uint32_t* pixel;
1536	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1537		BACKGROUND_BITMAP_ITERATE(160, 128);
1538
1539		if (!mosaicWait) {
1540			LOAD_16(color, (offset + (localX >> 8) + (localY >> 8) * 160) << 1, renderer->d.vram);
1541#ifndef COLOR_16_BIT
1542			unsigned color32 = 0;
1543			color32 |= (color << 9) & 0xF80000;
1544			color32 |= (color << 3) & 0xF8;
1545			color32 |= (color << 6) & 0xF800;
1546			color = color32;
1547#endif
1548			mosaicWait = mosaicH;
1549		} else {
1550			--mosaicWait;
1551		}
1552
1553		uint32_t current = *pixel;
1554		if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1555			if (!variant) {
1556				_compositeBlendObjwin(renderer, pixel, color | flags, current);
1557			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1558				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1559			} else if (renderer->blendEffect == BLEND_DARKEN) {
1560				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1561			}
1562		}
1563	}
1564}
1565
1566#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1567	SPRITE_YBASE_ ## DEPTH(inY); \
1568	unsigned tileData; \
1569	for (; outX < condition; ++outX, inX += xOffset) { \
1570		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1571			continue; \
1572		} \
1573		SPRITE_XBASE_ ## DEPTH(inX); \
1574		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1575	}
1576
1577#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1578	SPRITE_YBASE_ ## DEPTH(inY); \
1579	unsigned tileData; \
1580	if (outX % mosaicH) { \
1581		if (!inX && xOffset > 0) { \
1582			inX = mosaicH - (outX % mosaicH); \
1583			outX += mosaicH - (outX % mosaicH); \
1584		} else if (inX == width - xOffset) { \
1585			inX = mosaicH + (outX % mosaicH); \
1586			outX += mosaicH - (outX % mosaicH); \
1587		} \
1588	} \
1589	for (; outX < condition; ++outX, inX += xOffset) { \
1590		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1591			continue; \
1592		} \
1593		int localX = inX - xOffset * (outX % mosaicH); \
1594		if (localX < 0 || localX > width - 1) { \
1595			continue; \
1596		} \
1597		SPRITE_XBASE_ ## DEPTH(localX); \
1598		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1599	}
1600
1601#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1602	unsigned tileData; \
1603	for (; outX < x + totalWidth && outX < end; ++outX, ++inX) { \
1604		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1605			continue; \
1606		} \
1607		xAccum += mat.a; \
1608		yAccum += mat.c; \
1609		int localX = (xAccum >> 8) + (width >> 1); \
1610		int localY = (yAccum >> 8) + (height >> 1); \
1611		\
1612		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1613			continue; \
1614		} \
1615		\
1616		SPRITE_YBASE_ ## DEPTH(localY); \
1617		SPRITE_XBASE_ ## DEPTH(localX); \
1618		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1619	}
1620
1621#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1622#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1623
1624#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1625	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1626	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1627	current = renderer->spriteLayer[outX]; \
1628	if ((current & FLAG_ORDER_MASK) > flags) { \
1629		if (tileData) { \
1630			renderer->spriteLayer[outX] = palette[tileData] | flags; \
1631		} else if (current != FLAG_UNWRITTEN) { \
1632			renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
1633		} \
1634	}
1635
1636#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1637	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1638	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1639	if (tileData) { \
1640		renderer->row[outX] |= FLAG_OBJWIN; \
1641	}
1642
1643#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1644#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width : 0x80) + (localY & 0x7) * 8;
1645
1646#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1647	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1648	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1649	current = renderer->spriteLayer[outX]; \
1650	if ((current & FLAG_ORDER_MASK) > flags) { \
1651		if (tileData) { \
1652			renderer->spriteLayer[outX] = palette[tileData] | flags; \
1653		} else if (current != FLAG_UNWRITTEN) { \
1654			renderer->spriteLayer[outX] = (current & ~FLAG_ORDER_MASK) | GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY; \
1655		} \
1656	}
1657
1658#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1659	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1660	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1661	if (tileData) { \
1662		renderer->row[outX] |= FLAG_OBJWIN; \
1663	}
1664
1665static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1666	int width = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2];
1667	int height = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2 + 1];
1668	int start = renderer->start;
1669	int end = renderer->end;
1670	uint32_t flags = GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY;
1671	flags |= FLAG_TARGET_1 * ((GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
1672	flags |= FLAG_OBJWIN * (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN);
1673	int32_t x = GBAObjAttributesBGetX(sprite->b) << 23;
1674	x >>= 23;
1675	uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
1676	unsigned charBase = GBAObjAttributesCGetTile(sprite->c) * 0x20;
1677	int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1678	if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1679		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1680		variant = 0;
1681	}
1682	color_t* palette = &renderer->normalPalette[0x100];
1683	if (variant) {
1684		palette = &renderer->variantPalette[0x100];
1685	}
1686
1687	int inY = y - (int) GBAObjAttributesAGetY(sprite->a);
1688
1689	uint32_t current;
1690	if (GBAObjAttributesAIsTransformed(sprite->a)) {
1691		int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a);
1692		int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a);
1693		struct GBAOAMMatrix mat;
1694		LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
1695		LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
1696		LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
1697		LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
1698
1699		if (inY < 0) {
1700			inY += 256;
1701		}
1702		int outX = x >= start ? x : start;
1703		int inX = outX - x;
1704		int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1));
1705		int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1));
1706
1707		if (!GBAObjAttributesAIs256Color(sprite->a)) {
1708			palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1709			if (flags & FLAG_OBJWIN) {
1710				SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1711			} else {
1712				SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1713			}
1714		} else {
1715			if (flags & FLAG_OBJWIN) {
1716				SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1717			} else {
1718				SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1719			}
1720		}
1721	} else {
1722		int outX = x >= start ? x : start;
1723		int condition = x + width;
1724		int mosaicH = 1;
1725		if (GBAObjAttributesAIsMosaic(sprite->a)) {
1726			mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
1727			if (condition % mosaicH) {
1728				condition += mosaicH - (condition % mosaicH);
1729			}
1730		}
1731		if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) {
1732			inY += 256;
1733		}
1734		if (GBAObjAttributesBIsVFlip(sprite->b)) {
1735			inY = height - inY - 1;
1736		}
1737		if (end < condition) {
1738			condition = end;
1739		}
1740		int inX = outX - x;
1741		int xOffset = 1;
1742		if (GBAObjAttributesBIsHFlip(sprite->b)) {
1743			inX = width - inX - 1;
1744			xOffset = -1;
1745		}
1746		if (!GBAObjAttributesAIs256Color(sprite->a)) {
1747			palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1748			if (flags & FLAG_OBJWIN) {
1749				SPRITE_NORMAL_LOOP(16, OBJWIN);
1750			} else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1751				SPRITE_MOSAIC_LOOP(16, NORMAL);
1752			} else {
1753				SPRITE_NORMAL_LOOP(16, NORMAL);
1754			}
1755		} else {
1756			if (flags & FLAG_OBJWIN) {
1757				SPRITE_NORMAL_LOOP(256, OBJWIN);
1758			} else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1759				SPRITE_MOSAIC_LOOP(256, NORMAL);
1760			} else {
1761				SPRITE_NORMAL_LOOP(256, NORMAL);
1762			}
1763		}
1764	}
1765	return 1;
1766}
1767
1768static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1769	int x;
1770	uint32_t* pixel = &renderer->row[renderer->start];
1771	uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
1772
1773	int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt);
1774	bool objwinDisable = false;
1775	bool objwinOnly = false;
1776	if (objwinSlowPath) {
1777		objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed);
1778		objwinOnly = !objwinDisable && !GBAWindowControlIsObjEnable(renderer->currentWindow.packed);
1779
1780		if (objwinDisable) {
1781			for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1782				uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1783				uint32_t current = *pixel;
1784				if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1785					_compositeBlendObjwin(renderer, pixel, color | flags, current);
1786				}
1787			}
1788			return;
1789		} else if (objwinOnly) {
1790			for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1791				uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1792				uint32_t current = *pixel;
1793				if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1794					_compositeBlendObjwin(renderer, pixel, color | flags, current);
1795				}
1796			}
1797			return;
1798		} else {
1799			for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1800				uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1801				uint32_t current = *pixel;
1802				if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1803					_compositeBlendObjwin(renderer, pixel, color | flags, current);
1804				}
1805			}
1806			return;
1807		}
1808	}
1809	for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1810		uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1811		uint32_t current = *pixel;
1812		if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1813			_compositeBlendNoObjwin(renderer, pixel, color | flags, current);
1814		}
1815	}
1816}
1817
1818static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1819	int i;
1820	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1821		for (i = 0; i < 512; ++i) {
1822			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1823		}
1824	} else if (renderer->blendEffect == BLEND_DARKEN) {
1825		for (i = 0; i < 512; ++i) {
1826			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1827		}
1828	} else {
1829		for (i = 0; i < 512; ++i) {
1830			renderer->variantPalette[i] = renderer->normalPalette[i];
1831		}
1832	}
1833}
1834
1835static inline unsigned _brighten(unsigned color, int y) {
1836	unsigned c = 0;
1837	unsigned a;
1838#ifdef COLOR_16_BIT
1839	a = color & 0x1F;
1840	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1841
1842#ifdef COLOR_5_6_5
1843	a = color & 0x7C0;
1844	c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1845
1846	a = color & 0xF800;
1847	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1848#else
1849	a = color & 0x3E0;
1850	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1851
1852	a = color & 0x7C00;
1853	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1854#endif
1855#else
1856	a = color & 0xF8;
1857	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1858
1859	a = color & 0xF800;
1860	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1861
1862	a = color & 0xF80000;
1863	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1864#endif
1865	return c;
1866}
1867
1868static inline unsigned _darken(unsigned color, int y) {
1869	unsigned c = 0;
1870	unsigned a;
1871#ifdef COLOR_16_BIT
1872	a = color & 0x1F;
1873	c |= (a - (a * y) / 16) & 0x1F;
1874
1875#ifdef COLOR_5_6_5
1876	a = color & 0x7C0;
1877	c |= (a - (a * y) / 16) & 0x7C0;
1878
1879	a = color & 0xF800;
1880	c |= (a - (a * y) / 16) & 0xF800;
1881#else
1882	a = color & 0x3E0;
1883	c |= (a - (a * y) / 16) & 0x3E0;
1884
1885	a = color & 0x7C00;
1886	c |= (a - (a * y) / 16) & 0x7C00;
1887#endif
1888#else
1889	a = color & 0xF8;
1890	c |= (a - (a * y) / 16) & 0xF8;
1891
1892	a = color & 0xF800;
1893	c |= (a - (a * y) / 16) & 0xF800;
1894
1895	a = color & 0xF80000;
1896	c |= (a - (a * y) / 16) & 0xF80000;
1897#endif
1898	return c;
1899}
1900
1901static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1902	unsigned c = 0;
1903	unsigned a, b;
1904#ifdef COLOR_16_BIT
1905#ifdef COLOR_5_6_5
1906	a = colorA & 0xF81F;
1907	b = colorB & 0xF81F;
1908	a |= (colorA & 0x7C0) << 16;
1909	b |= (colorB & 0x7C0) << 16;
1910	c = ((a * weightA + b * weightB) / 16);
1911	if (c & 0x08000000) {
1912		c = (c & ~0x0FC00000) | 0x07C00000;
1913	}
1914	if (c & 0x0020) {
1915		c = (c & ~0x003F) | 0x001F;
1916	}
1917	if (c & 0x10000) {
1918		c = (c & ~0x1F800) | 0xF800;
1919	}
1920	c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
1921#else
1922	a = colorA & 0x7C1F;
1923	b = colorB & 0x7C1F;
1924	a |= (colorA & 0x3E0) << 16;
1925	b |= (colorB & 0x3E0) << 16;
1926	c = ((a * weightA + b * weightB) / 16);
1927	if (c & 0x04000000) {
1928		c = (c & ~0x07E00000) | 0x03E00000;
1929	}
1930	if (c & 0x0020) {
1931		c = (c & ~0x003F) | 0x001F;
1932	}
1933	if (c & 0x10000) {
1934		c = (c & ~0x1F800) | 0xF800;
1935	}
1936	c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
1937#endif
1938#else
1939	a = colorA & 0xF8;
1940	b = colorB & 0xF8;
1941	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1942	if (c & 0x00000100) {
1943		c = 0x000000F8;
1944	}
1945
1946	a = colorA & 0xF800;
1947	b = colorB & 0xF800;
1948	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1949	if (c & 0x00010000) {
1950		c = (c & 0x000000F8) | 0x0000F800;
1951	}
1952
1953	a = colorA & 0xF80000;
1954	b = colorB & 0xF80000;
1955	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1956	if (c & 0x01000000) {
1957		c = (c & 0x0000F8F8) | 0x00F80000;
1958	}
1959#endif
1960	return c;
1961}