all repos — mgba @ 9dc49df0bca23925cfcc8193a1770463fd9916cf

mGBA Game Boy Advance Emulator

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

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