all repos — mgba @ ae12dd907c4553de0d72f7da66135a39b70767ba

mGBA Game Boy Advance Emulator

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

   1#include "video-software.h"
   2
   3#include "gba.h"
   4#include "gba-io.h"
   5
   6#define UNUSED(X) (void) (X)
   7
   8static const int _objSizes[32] = {
   9	8, 8,
  10	16, 16,
  11	32, 32,
  12	64, 64,
  13	16, 8,
  14	32, 8,
  15	32, 16,
  16	64, 32,
  17	8, 16,
  18	8, 32,
  19	16, 32,
  20	32, 64,
  21	0, 0,
  22	0, 0,
  23	0, 0,
  24	0, 0
  25};
  26
  27static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
  28static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
  29static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
  30static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
  31static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
  32static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
  33static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
  34
  35static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
  36static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
  37static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  38static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  39static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  40static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  41static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  42static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  43static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  44static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  45static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
  46
  47static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
  48static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  49static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  50static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  51static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  52static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  53static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
  54static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
  55static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
  56static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
  57
  58static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
  59static inline unsigned _brighten(unsigned color, int y);
  60static inline unsigned _darken(unsigned color, int y);
  61static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);
  62
  63void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
  64	renderer->d.init = GBAVideoSoftwareRendererInit;
  65	renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
  66	renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
  67	renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
  68	renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
  69	renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
  70	renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
  71}
  72
  73static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
  74	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
  75	int i;
  76
  77	softwareRenderer->dispcnt.packed = 0x0080;
  78
  79	softwareRenderer->target1Obj = 0;
  80	softwareRenderer->target1Bd = 0;
  81	softwareRenderer->target2Obj = 0;
  82	softwareRenderer->target2Bd = 0;
  83	softwareRenderer->blendEffect = BLEND_NONE;
  84	memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
  85	memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
  86
  87	softwareRenderer->blda = 0;
  88	softwareRenderer->bldb = 0;
  89	softwareRenderer->bldy = 0;
  90
  91	softwareRenderer->winN[0].control.priority = 0;
  92	softwareRenderer->winN[1].control.priority = 1;
  93	softwareRenderer->objwin.priority = 2;
  94	softwareRenderer->winout.priority = 3;
  95
  96	softwareRenderer->mosaic.packed = 0;
  97
  98	for (i = 0; i < 4; ++i) {
  99		struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
 100		bg->index = i;
 101		bg->enabled = 0;
 102		bg->priority = 0;
 103		bg->charBase = 0;
 104		bg->mosaic = 0;
 105		bg->multipalette = 0;
 106		bg->screenBase = 0;
 107		bg->overflow = 0;
 108		bg->size = 0;
 109		bg->target1 = 0;
 110		bg->target2 = 0;
 111		bg->x = 0;
 112		bg->y = 0;
 113		bg->refx = 0;
 114		bg->refy = 0;
 115		bg->dx = 256;
 116		bg->dmx = 0;
 117		bg->dy = 0;
 118		bg->dmy = 256;
 119		bg->sx = 0;
 120		bg->sy = 0;
 121	}
 122}
 123
 124static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
 125	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 126	UNUSED(softwareRenderer);
 127}
 128
 129static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 130	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 131	switch (address) {
 132	case REG_DISPCNT:
 133		softwareRenderer->dispcnt.packed = value;
 134		GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
 135		break;
 136	case REG_BG0CNT:
 137		value &= 0xFFCF;
 138		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
 139		break;
 140	case REG_BG1CNT:
 141		value &= 0xFFCF;
 142		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
 143		break;
 144	case REG_BG2CNT:
 145		value &= 0xFFCF;
 146		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
 147		break;
 148	case REG_BG3CNT:
 149		value &= 0xFFCF;
 150		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
 151		break;
 152	case REG_BG0HOFS:
 153		value &= 0x01FF;
 154		softwareRenderer->bg[0].x = value;
 155		break;
 156	case REG_BG0VOFS:
 157		value &= 0x01FF;
 158		softwareRenderer->bg[0].y = value;
 159		break;
 160	case REG_BG1HOFS:
 161		value &= 0x01FF;
 162		softwareRenderer->bg[1].x = value;
 163		break;
 164	case REG_BG1VOFS:
 165		value &= 0x01FF;
 166		softwareRenderer->bg[1].y = value;
 167		break;
 168	case REG_BG2HOFS:
 169		value &= 0x01FF;
 170		softwareRenderer->bg[2].x = value;
 171		break;
 172	case REG_BG2VOFS:
 173		value &= 0x01FF;
 174		softwareRenderer->bg[2].y = value;
 175		break;
 176	case REG_BG3HOFS:
 177		value &= 0x01FF;
 178		softwareRenderer->bg[3].x = value;
 179		break;
 180	case REG_BG3VOFS:
 181		value &= 0x01FF;
 182		softwareRenderer->bg[3].y = value;
 183		break;
 184	case REG_BG2PA:
 185		GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
 186		break;
 187	case REG_BG2PB:
 188		GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
 189		break;
 190	case REG_BG2PC:
 191		GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
 192		break;
 193	case REG_BG2PD:
 194		GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
 195		break;
 196	case REG_BG2X_LO:
 197		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
 198		break;
 199	case REG_BG2X_HI:
 200		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
 201		break;
 202	case REG_BG2Y_LO:
 203		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
 204		break;
 205	case REG_BG2Y_HI:
 206		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
 207		break;
 208	case REG_BG3PA:
 209		GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
 210		break;
 211	case REG_BG3PB:
 212		GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
 213		break;
 214	case REG_BG3PC:
 215		GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
 216		break;
 217	case REG_BG3PD:
 218		GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
 219		break;
 220	case REG_BG3X_LO:
 221		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
 222		break;
 223	case REG_BG3X_HI:
 224		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
 225		break;
 226	case REG_BG3Y_LO:
 227		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
 228		break;
 229	case REG_BG3Y_HI:
 230		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
 231		break;
 232	case REG_BLDCNT:
 233		GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
 234		break;
 235	case REG_BLDALPHA:
 236		softwareRenderer->blda = value & 0x1F;
 237		if (softwareRenderer->blda > 0x10) {
 238			softwareRenderer->blda = 0x10;
 239		}
 240		softwareRenderer->bldb = (value >> 8) & 0x1F;
 241		if (softwareRenderer->bldb > 0x10) {
 242			softwareRenderer->bldb = 0x10;
 243		}
 244		break;
 245	case REG_BLDY:
 246		softwareRenderer->bldy = value & 0x1F;
 247		if (softwareRenderer->bldy > 0x10) {
 248			softwareRenderer->bldy = 0x10;
 249		}
 250		_updatePalettes(softwareRenderer);
 251		break;
 252	case REG_WIN0H:
 253		softwareRenderer->winN[0].h.packed = value;
 254		if (softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end || softwareRenderer->winN[0].h.end > VIDEO_HORIZONTAL_PIXELS) {
 255			softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS;
 256		}
 257		break;
 258	case REG_WIN1H:
 259		softwareRenderer->winN[1].h.packed = value;
 260		if (softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end || softwareRenderer->winN[1].h.end > VIDEO_HORIZONTAL_PIXELS) {
 261			softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS;
 262		}
 263		break;
 264	case REG_WIN0V:
 265		softwareRenderer->winN[0].v.packed = value;
 266		if (softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end || softwareRenderer->winN[0].v.end > VIDEO_HORIZONTAL_PIXELS) {
 267			softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
 268		}
 269		break;
 270	case REG_WIN1V:
 271		softwareRenderer->winN[1].v.packed = value;
 272		if (softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end || softwareRenderer->winN[1].v.end > VIDEO_HORIZONTAL_PIXELS) {
 273			softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
 274		}
 275		break;
 276	case REG_WININ:
 277		softwareRenderer->winN[0].control.packed = value;
 278		softwareRenderer->winN[1].control.packed = value >> 8;
 279		break;
 280	case REG_WINOUT:
 281		softwareRenderer->winout.packed = value;
 282		softwareRenderer->objwin.packed = value >> 8;
 283		break;
 284	case REG_MOSAIC:
 285		softwareRenderer->mosaic.packed = value;
 286		break;
 287	case REG_GREENSWP:
 288		GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);
 289		break;
 290	default:
 291		GBALog(0, GBA_LOG_GAME_ERROR, "Invalid video register: 0x%03X", address);
 292	}
 293	return value;
 294}
 295
 296static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
 297	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 298	softwareRenderer->oamDirty = 1;
 299	UNUSED(oam);
 300}
 301
 302static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 303	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 304#ifdef COLOR_16_BIT
 305#ifdef COLOR_5_6_5
 306	unsigned color = 0;
 307	color |= (value & 0x001F) << 11;
 308	color |= (value & 0x03E0) << 1;
 309	color |= (value & 0x7C00) >> 10;
 310#else
 311	unsigned color = value;
 312#endif
 313#else
 314	unsigned color = 0;
 315	color |= (value << 3) & 0xF8;
 316	color |= (value << 6) & 0xF800;
 317	color |= (value << 9) & 0xF80000;
 318#endif
 319	softwareRenderer->normalPalette[address >> 1] = color;
 320	if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
 321		softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
 322	} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
 323		softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
 324	}
 325}
 326
 327static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
 328	int activeWindow;
 329	int startX = 0;
 330	if (win->h.end > 0) {
 331		for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
 332			if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
 333				// Insert a window before the end of the active window
 334				struct Window oldWindow = softwareRenderer->windows[activeWindow];
 335				if (win->h.start > startX) {
 336					// And after the start of the active window
 337					int nextWindow = softwareRenderer->nWindows;
 338					++softwareRenderer->nWindows;
 339					for (; nextWindow > activeWindow; --nextWindow) {
 340						softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
 341					}
 342					softwareRenderer->windows[activeWindow].endX = win->h.start;
 343					++activeWindow;
 344				}
 345				softwareRenderer->windows[activeWindow].control = win->control;
 346				softwareRenderer->windows[activeWindow].endX = win->h.end;
 347				if (win->h.end >= oldWindow.endX) {
 348					// Trim off extra windows we've overwritten
 349					for (++activeWindow; win->h.end >= softwareRenderer->windows[activeWindow].endX && softwareRenderer->nWindows > activeWindow; ++activeWindow) {
 350						softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
 351						--softwareRenderer->nWindows;
 352					}
 353				} else {
 354					++activeWindow;
 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] = oldWindow;
 361				}
 362				break;
 363			}
 364			startX = softwareRenderer->windows[activeWindow].endX;
 365		}
 366	}
 367}
 368
 369static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
 370	int i;
 371	int oamMax = 0;
 372	for (i = 0; i < 128; ++i) {
 373		struct GBAObj* obj = &renderer->d.oam->obj[i];
 374		if (obj->transformed || !obj->disable) {
 375			int height = _objSizes[obj->shape * 8 + obj->size * 2 + 1];
 376			if (obj->transformed) {
 377				height <<= ((struct GBATransformedObj*) obj)->doublesize;
 378			}
 379			if (obj->y < VIDEO_VERTICAL_PIXELS || obj->y + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
 380				renderer->sprites[oamMax].y = obj->y;
 381				renderer->sprites[oamMax].endY = obj->y + height;
 382				renderer->sprites[oamMax].obj = *obj;
 383				++oamMax;
 384			}
 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
1547	int objwinSlowPath = renderer->dispcnt.objwinEnable;
1548	int objwinDisable = 0;
1549	if (objwinSlowPath) {
1550		objwinDisable = !renderer->objwin.objEnable;
1551	}
1552	if (objwinSlowPath && objwinDisable) {
1553		for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1554			uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1555			uint32_t current = *pixel;
1556			if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1557				_compositeBlendObjwin(renderer, pixel, color | flags, current);
1558			}
1559		}
1560	} else {
1561		for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1562			uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1563			uint32_t current = *pixel;
1564			if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1565				_compositeBlendNoObjwin(renderer, pixel, color | flags, current);
1566			}
1567		}
1568	}
1569}
1570
1571static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1572	int i;
1573	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1574		for (i = 0; i < 512; ++i) {
1575			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1576		}
1577	} else if (renderer->blendEffect == BLEND_DARKEN) {
1578		for (i = 0; i < 512; ++i) {
1579			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1580		}
1581	} else {
1582		for (i = 0; i < 512; ++i) {
1583			renderer->variantPalette[i] = renderer->normalPalette[i];
1584		}
1585	}
1586}
1587
1588static inline unsigned _brighten(unsigned color, int y) {
1589	unsigned c = 0;
1590	unsigned a;
1591#ifdef COLOR_16_BIT
1592	a = color & 0x1F;
1593	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1594
1595#ifdef COLOR_5_6_5
1596	a = color & 0x7C0;
1597	c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1598
1599	a = color & 0xF800;
1600	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1601#else
1602	a = color & 0x3E0;
1603	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1604
1605	a = color & 0x7C00;
1606	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1607#endif
1608#else
1609	a = color & 0xF8;
1610	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1611
1612	a = color & 0xF800;
1613	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1614
1615	a = color & 0xF80000;
1616	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1617#endif
1618	return c;
1619}
1620
1621static inline unsigned _darken(unsigned color, int y) {
1622	unsigned c = 0;
1623	unsigned a;
1624#ifdef COLOR_16_BIT
1625	a = color & 0x1F;
1626	c |= (a - (a * y) / 16) & 0x1F;
1627
1628#ifdef COLOR_5_6_5
1629	a = color & 0x7C0;
1630	c |= (a - (a * y) / 16) & 0x7C0;
1631
1632	a = color & 0xF800;
1633	c |= (a - (a * y) / 16) & 0xF800;
1634#else
1635	a = color & 0x3E0;
1636	c |= (a - (a * y) / 16) & 0x3E0;
1637
1638	a = color & 0x7C00;
1639	c |= (a - (a * y) / 16) & 0x7C00;
1640#endif
1641#else
1642	a = color & 0xF8;
1643	c |= (a - (a * y) / 16) & 0xF8;
1644
1645	a = color & 0xF800;
1646	c |= (a - (a * y) / 16) & 0xF800;
1647
1648	a = color & 0xF80000;
1649	c |= (a - (a * y) / 16) & 0xF80000;
1650#endif
1651	return c;
1652}
1653
1654static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1655	unsigned c = 0;
1656	unsigned a, b;
1657#ifdef COLOR_16_BIT
1658#ifdef COLOR_5_6_5
1659	a = colorA & 0xF81F;
1660	b = colorB & 0xF81F;
1661	a |= (colorA & 0x7C0) << 16;
1662	b |= (colorB & 0x7C0) << 16;
1663	c = ((a * weightA + b * weightB) / 16);
1664	if (c & 0x08000000) {
1665		c = (c & ~0x0FC00000) | 0x07C00000;
1666	}
1667	if (c & 0x0020) {
1668		c = (c & ~0x003F) | 0x001F;
1669	}
1670	if (c & 0x10000) {
1671		c = (c & ~0x1F800) | 0xF800;
1672	}
1673	c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
1674#else
1675	a = colorA & 0x7C1F;
1676	b = colorB & 0x7C1F;
1677	a |= (colorA & 0x3E0) << 16;
1678	b |= (colorB & 0x3E0) << 16;
1679	c = ((a * weightA + b * weightB) / 16);
1680	if (c & 0x04000000) {
1681		c = (c & ~0x07E00000) | 0x03E00000;
1682	}
1683	if (c & 0x0020) {
1684		c = (c & ~0x003F) | 0x001F;
1685	}
1686	if (c & 0x10000) {
1687		c = (c & ~0x1F800) | 0xF800;
1688	}
1689	c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
1690#endif
1691#else
1692	a = colorA & 0xF8;
1693	b = colorB & 0xF8;
1694	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1695	if (c & 0x00000100) {
1696		c = 0x000000F8;
1697	}
1698
1699	a = colorA & 0xF800;
1700	b = colorB & 0xF800;
1701	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1702	if (c & 0x00010000) {
1703		c = (c & 0x000000F8) | 0x0000F800;
1704	}
1705
1706	a = colorA & 0xF80000;
1707	b = colorB & 0xF80000;
1708	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1709	if (c & 0x01000000) {
1710		c = (c & 0x0000F8F8) | 0x00F80000;
1711	}
1712#endif
1713	return c;
1714}