all repos — mgba @ 4255d10f0864362265c8c1659c0a89fee027cf98

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