all repos — mgba @ eb81fc3c65f918c16f0e9ddeeb6cbea662155731

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