all repos — mgba @ e9a2b2a57c4f478fb3600fe9d87e1b97804d73de

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