all repos — mgba @ 7142a0f820eff4f4d1aae1a45e9d6c74f8b5219b

mGBA Game Boy Advance Emulator

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

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