all repos — mgba @ 6456a886695e6290fee373255fef014f47c20562

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