all repos — mgba @ 4f04c30f708f30f51f5b90aff2e320cd607955ea

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