all repos — mgba @ 2161769660d42101988e82be003d3555995e2ec9

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