all repos — mgba @ 6ad2b673b1110329517fc540952579ab8f8cc5e2

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			if (obj->y < VIDEO_VERTICAL_PIXELS || obj->y + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
 382				renderer->sprites[oamMax].y = obj->y;
 383				renderer->sprites[oamMax].endY = obj->y + height;
 384				renderer->sprites[oamMax].obj = *obj;
 385				++oamMax;
 386			}
 387		}
 388	}
 389	renderer->oamMax = oamMax;
 390	renderer->oamDirty = 0;
 391}
 392
 393
 394static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
 395	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 396
 397	color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
 398	if (softwareRenderer->dispcnt.forcedBlank) {
 399		int x;
 400		for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 401			row[x] = GBA_COLOR_WHITE;
 402		}
 403		return;
 404	}
 405
 406	int x;
 407	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; x += 4) {
 408		softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
 409		softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
 410		softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
 411		softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
 412	}
 413
 414	softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
 415	softwareRenderer->nWindows = 1;
 416	if (softwareRenderer->dispcnt.win0Enable || softwareRenderer->dispcnt.win1Enable || softwareRenderer->dispcnt.objwinEnable) {
 417		softwareRenderer->windows[0].control = softwareRenderer->winout;
 418		if (softwareRenderer->dispcnt.win1Enable && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) {
 419			_breakWindow(softwareRenderer, &softwareRenderer->winN[1]);
 420		}
 421		if (softwareRenderer->dispcnt.win0Enable && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) {
 422			_breakWindow(softwareRenderer, &softwareRenderer->winN[0]);
 423		}
 424	} else {
 425		softwareRenderer->windows[0].control.packed = 0xFF;
 426	}
 427
 428	int w;
 429	x = 0;
 430	for (w = 0; w < softwareRenderer->nWindows; ++w) {
 431		// TOOD: handle objwin on backdrop
 432		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
 433		if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
 434			backdrop |= softwareRenderer->normalPalette[0];
 435		} else {
 436			backdrop |= softwareRenderer->variantPalette[0];
 437		}
 438		int end = softwareRenderer->windows[w].endX;
 439		for (; x < end; ++x) {
 440			softwareRenderer->row[x] = backdrop;
 441		}
 442	}
 443
 444	_drawScanline(softwareRenderer, y);
 445
 446	if (softwareRenderer->target2Bd) {
 447		x = 0;
 448		for (w = 0; w < softwareRenderer->nWindows; ++w) {
 449		uint32_t backdrop = FLAG_UNWRITTEN;
 450			if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
 451				backdrop |= softwareRenderer->normalPalette[0];
 452			} else {
 453				backdrop |= softwareRenderer->variantPalette[0];
 454			}
 455			int end = softwareRenderer->windows[w].endX;
 456			for (; x < end; ++x) {
 457				uint32_t color = softwareRenderer->row[x];
 458				if (color & FLAG_TARGET_1) {
 459					softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
 460				}
 461			}
 462		}
 463	}
 464
 465#ifdef COLOR_16_BIT
 466#ifdef __arm__
 467	_to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
 468#else
 469	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 470		row[x] = softwareRenderer->row[x];
 471	}
 472#endif
 473#else
 474	memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
 475#endif
 476}
 477
 478static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
 479	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 480
 481	softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
 482	softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
 483	softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
 484	softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
 485}
 486
 487static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
 488	renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
 489	renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
 490	renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
 491	renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
 492}
 493
 494static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 495	UNUSED(renderer);
 496	union GBARegisterBGCNT reg = { .packed = value };
 497	bg->priority = reg.priority;
 498	bg->charBase = reg.charBase << 14;
 499	bg->mosaic = reg.mosaic;
 500	bg->multipalette = reg.multipalette;
 501	bg->screenBase = reg.screenBase << 11;
 502	bg->overflow = reg.overflow;
 503	bg->size = reg.size;
 504}
 505
 506static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 507	bg->dx = value;
 508}
 509
 510static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 511	bg->dmx = value;
 512}
 513
 514static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 515	bg->dy = value;
 516}
 517
 518static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 519	bg->dmy = value;
 520}
 521
 522static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 523	bg->refx = (bg->refx & 0xFFFF0000) | value;
 524	bg->sx = bg->refx;
 525}
 526
 527static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 528	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
 529	bg->refx <<= 4;
 530	bg->refx >>= 4;
 531	bg->sx = bg->refx;
 532}
 533
 534static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 535	bg->refy = (bg->refy & 0xFFFF0000) | value;
 536	bg->sy = bg->refy;
 537}
 538
 539static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 540	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
 541	bg->refy <<= 4;
 542	bg->refy >>= 4;
 543	bg->sy = bg->refy;
 544}
 545
 546static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
 547	union {
 548		struct {
 549			unsigned target1Bg0 : 1;
 550			unsigned target1Bg1 : 1;
 551			unsigned target1Bg2 : 1;
 552			unsigned target1Bg3 : 1;
 553			unsigned target1Obj : 1;
 554			unsigned target1Bd : 1;
 555			enum BlendEffect effect : 2;
 556			unsigned target2Bg0 : 1;
 557			unsigned target2Bg1 : 1;
 558			unsigned target2Bg2 : 1;
 559			unsigned target2Bg3 : 1;
 560			unsigned target2Obj : 1;
 561			unsigned target2Bd : 1;
 562		};
 563		uint16_t packed;
 564	} bldcnt = { .packed = value };
 565
 566	enum BlendEffect oldEffect = renderer->blendEffect;
 567
 568	renderer->bg[0].target1 = bldcnt.target1Bg0;
 569	renderer->bg[1].target1 = bldcnt.target1Bg1;
 570	renderer->bg[2].target1 = bldcnt.target1Bg2;
 571	renderer->bg[3].target1 = bldcnt.target1Bg3;
 572	renderer->bg[0].target2 = bldcnt.target2Bg0;
 573	renderer->bg[1].target2 = bldcnt.target2Bg1;
 574	renderer->bg[2].target2 = bldcnt.target2Bg2;
 575	renderer->bg[3].target2 = bldcnt.target2Bg3;
 576
 577	renderer->blendEffect = bldcnt.effect;
 578	renderer->target1Obj = bldcnt.target1Obj;
 579	renderer->target1Bd = bldcnt.target1Bd;
 580	renderer->target2Obj = bldcnt.target2Obj;
 581	renderer->target2Bd = bldcnt.target2Bd;
 582
 583	renderer->anyTarget2 = bldcnt.packed & 0x3F00;
 584
 585	if (oldEffect != renderer->blendEffect) {
 586		_updatePalettes(renderer);
 587	}
 588}
 589
 590#define TEST_LAYER_ENABLED(X) \
 591	(renderer->bg[X].enabled && \
 592	(renderer->currentWindow.bg ## X ## Enable || \
 593	(renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
 594	renderer->bg[X].priority == priority)
 595
 596static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
 597	int w;
 598	renderer->end = 0;
 599	int spriteLayers = 0;
 600	if (renderer->dispcnt.objEnable) {
 601		if (renderer->oamDirty) {
 602			_cleanOAM(renderer);
 603		}
 604		int mosaicV = renderer->mosaic.objV + 1;
 605		int mosaicY = y - (y % mosaicV);
 606		for (w = 0; w < renderer->nWindows; ++w) {
 607			renderer->start = renderer->end;
 608			renderer->end = renderer->windows[w].endX;
 609			renderer->currentWindow = renderer->windows[w].control;
 610			if (!renderer->currentWindow.objEnable) {
 611				continue;
 612			}
 613			int i;
 614			int drawn;
 615			for (i = 0; i < renderer->oamMax; ++i) {
 616				int localY = y;
 617				struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
 618				if (sprite->obj.mosaic) {
 619					localY = mosaicY;
 620				}
 621				if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
 622					continue;
 623				}
 624				if (sprite->obj.transformed) {
 625					drawn = _preprocessTransformedSprite(renderer, &sprite->tobj, localY);
 626				} else {
 627					drawn = _preprocessSprite(renderer, &sprite->obj, localY);
 628				}
 629				spriteLayers |= drawn << sprite->obj.priority;
 630			}
 631		}
 632	}
 633
 634	int priority;
 635	for (priority = 0; priority < 4; ++priority) {
 636		if (spriteLayers & (1 << priority)) {
 637			_postprocessSprite(renderer, priority);
 638		}
 639		renderer->end = 0;
 640		for (w = 0; w < renderer->nWindows; ++w) {
 641			renderer->start = renderer->end;
 642			renderer->end = renderer->windows[w].endX;
 643			renderer->currentWindow = renderer->windows[w].control;
 644			if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
 645				_drawBackgroundMode0(renderer, &renderer->bg[0], y);
 646			}
 647			if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
 648				_drawBackgroundMode0(renderer, &renderer->bg[1], y);
 649			}
 650			if (TEST_LAYER_ENABLED(2)) {
 651				switch (renderer->dispcnt.mode) {
 652				case 0:
 653					_drawBackgroundMode0(renderer, &renderer->bg[2], y);
 654					break;
 655				case 1:
 656				case 2:
 657					_drawBackgroundMode2(renderer, &renderer->bg[2], y);
 658					break;
 659				case 3:
 660					_drawBackgroundMode3(renderer, &renderer->bg[2], y);
 661					break;
 662				case 4:
 663					_drawBackgroundMode4(renderer, &renderer->bg[2], y);
 664					break;
 665				case 5:
 666					_drawBackgroundMode5(renderer, &renderer->bg[2], y);
 667					break;
 668				}
 669			}
 670			if (TEST_LAYER_ENABLED(3)) {
 671				switch (renderer->dispcnt.mode) {
 672				case 0:
 673					_drawBackgroundMode0(renderer, &renderer->bg[3], y);
 674					break;
 675				case 2:
 676					_drawBackgroundMode2(renderer, &renderer->bg[3], y);
 677					break;
 678				}
 679			}
 680		}
 681	}
 682	renderer->bg[2].sx += renderer->bg[2].dmx;
 683	renderer->bg[2].sy += renderer->bg[2].dmy;
 684	renderer->bg[3].sx += renderer->bg[3].dmx;
 685	renderer->bg[3].sy += renderer->bg[3].dmy;
 686}
 687
 688// We stash the priority on the top bits so we can do a one-operator comparison
 689// The lower the number, the higher the priority, and sprites take precendence over backgrounds
 690// We want to do special processing if the color pixel is target 1, however
 691
 692static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 693	if (color >= current) {
 694		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 695			color = _mix(renderer->blda, current, renderer->bldb, color);
 696		} else {
 697			color = current & 0x00FFFFFF;
 698		}
 699	} else {
 700		color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
 701	}
 702	*pixel = color;
 703}
 704
 705static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 706	// We stash the priority on the top bits so we can do a one-operator comparison
 707	// The lower the number, the higher the priority, and sprites take precendence over backgrounds
 708	// We want to do special processing if the color pixel is target 1, however
 709	if (color >= current) {
 710		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 711			color = _mix(renderer->blda, current, renderer->bldb, color);
 712		} else {
 713			color = current & 0x00FFFFFF;
 714		}
 715	} else {
 716		color = color & ~FLAG_TARGET_2;
 717	}
 718	*pixel = color;
 719}
 720
 721static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 722	UNUSED(renderer);
 723	if (color < current) {
 724		*pixel = color | (current & FLAG_OBJWIN);
 725	}
 726}
 727
 728static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 729	UNUSED(renderer);
 730	if (color < current) {
 731		*pixel = color;
 732	}
 733}
 734
 735#define COMPOSITE_16_OBJWIN(BLEND) \
 736	if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
 737		unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
 738		_composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
 739	}
 740
 741#define COMPOSITE_16_NO_OBJWIN(BLEND) \
 742	_composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
 743
 744#define COMPOSITE_256_OBJWIN(BLEND) \
 745	if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
 746		unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
 747		_composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
 748	}
 749
 750#define COMPOSITE_256_NO_OBJWIN(BLEND) \
 751	COMPOSITE_16_NO_OBJWIN(BLEND)
 752
 753
 754#define COMPOSITE_16_NO_OBJWIN(BLEND) \
 755	_composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
 756
 757#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \
 758	pixelData = tileData & 0xF; \
 759	current = *pixel; \
 760	if (pixelData && IS_WRITABLE(current)) { \
 761		COMPOSITE_16_ ## OBJWIN (BLEND); \
 762	} \
 763	tileData >>= 4;
 764
 765#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN) \
 766	pixelData = tileData & 0xFF; \
 767	current = *pixel; \
 768	if (pixelData && IS_WRITABLE(current)) { \
 769		COMPOSITE_256_ ## OBJWIN (BLEND); \
 770	} \
 771	tileData >>= 8;
 772
 773#define BACKGROUND_TEXT_SELECT_CHARACTER \
 774	localX = tileX * 8 + inX; \
 775	xBase = localX & 0xF8; \
 776	if (background->size & 1) { \
 777		xBase += (localX & 0x100) << 5; \
 778	} \
 779	screenBase = yBase + (xBase >> 3); \
 780	mapData = renderer->d.vram[screenBase]; \
 781	localY = inY & 0x7; \
 782	if (GBA_TEXT_MAP_VFLIP(mapData)) { \
 783		localY = 7 - localY; \
 784	}
 785
 786#define PREPARE_OBJWIN \
 787	int objwinSlowPath = renderer->dispcnt.objwinEnable; \
 788	int objwinOnly = 0; \
 789	int objwinForceEnable = 0; \
 790	color_t* objwinPalette; \
 791	if (objwinSlowPath) { \
 792		if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
 793			objwinPalette = renderer->variantPalette; \
 794		} else { \
 795			objwinPalette = renderer->normalPalette; \
 796		} \
 797		switch (background->index) { \
 798		case 0: \
 799			objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \
 800			objwinOnly = !renderer->objwin.bg0Enable; \
 801			break; \
 802		case 1: \
 803			objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \
 804			objwinOnly = !renderer->objwin.bg1Enable; \
 805			break; \
 806		case 2: \
 807			objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \
 808			objwinOnly = !renderer->objwin.bg2Enable; \
 809			break; \
 810		case 3: \
 811			objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \
 812			objwinOnly = !renderer->objwin.bg3Enable; \
 813			break; \
 814		} \
 815	}
 816
 817#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
 818	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 819	palette = &mainPalette[paletteData]; \
 820	charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
 821	tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 822	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 823		tileData >>= 4 * mod8; \
 824		for (; outX < end; ++outX) { \
 825			uint32_t* pixel = &renderer->row[outX]; \
 826			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 827		} \
 828	} else { \
 829		for (outX = end - 1; outX >= renderer->start; --outX) { \
 830			uint32_t* pixel = &renderer->row[outX]; \
 831			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 832		} \
 833	}
 834
 835#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
 836	charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
 837	tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 838	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 839	palette = &mainPalette[paletteData]; \
 840	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 841		outX = renderer->end - mod8; \
 842		if (outX < renderer->start) { \
 843			tileData >>= 4 * (renderer->start - outX); \
 844			outX = renderer->start; \
 845		} \
 846		for (; outX < renderer->end; ++outX) { \
 847			uint32_t* pixel = &renderer->row[outX]; \
 848			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 849		} \
 850	} else { \
 851		tileData >>= 4 * (0x8 - mod8); \
 852		int end2 = renderer->end - 8; \
 853		if (end2 < -1) { \
 854			end2 = -1; \
 855		} \
 856		for (outX = renderer->end - 1; outX > end2; --outX) { \
 857			uint32_t* pixel = &renderer->row[outX]; \
 858			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 859		} \
 860	}
 861
 862#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
 863	for (; tileX < tileEnd; ++tileX) { \
 864		BACKGROUND_TEXT_SELECT_CHARACTER; \
 865		charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
 866		tileData = carryData; \
 867		for (x = 0; x < 8; ++x) { \
 868			if (!mosaicWait) { \
 869				paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 870				palette = &mainPalette[paletteData]; \
 871				tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 872				if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 873					tileData >>= x * 4; \
 874				} else { \
 875					tileData >>= (7 - x) * 4; \
 876				} \
 877				tileData &= 0xF; \
 878				tileData |= tileData << 4; \
 879				tileData |= tileData << 8; \
 880				tileData |= tileData << 12; \
 881				tileData |= tileData << 16; \
 882				tileData |= tileData << 20; \
 883				tileData |= tileData << 24; \
 884				tileData |= tileData << 28; \
 885				carryData = tileData; \
 886				mosaicWait = mosaicH; \
 887			} \
 888			--mosaicWait; \
 889			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 890			++pixel; \
 891		} \
 892	}
 893
 894#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
 895	for (; tileX < tileEnd; ++tileX) { \
 896		BACKGROUND_TEXT_SELECT_CHARACTER; \
 897		paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 898		palette = &mainPalette[paletteData]; \
 899		charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \
 900		tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 901		if (tileData) { \
 902			if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 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				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 918				++pixel; \
 919			} else { \
 920				pixel += 7; \
 921				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 922				--pixel; \
 923				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 924				--pixel; \
 925				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 926				--pixel; \
 927				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 928				--pixel; \
 929				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 930				--pixel; \
 931				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 932				--pixel; \
 933				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 934				--pixel; \
 935				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 936				pixel += 8; \
 937			} \
 938		} else { \
 939			pixel += 8; \
 940		} \
 941	}
 942
 943#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
 944	/* TODO: hflip */ \
 945	charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
 946	int end2 = end - 4; \
 947	int shift = inX & 0x3; \
 948	if (end2 > 0) { \
 949		tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 950		tileData >>= 8 * shift; \
 951		shift = 0; \
 952		for (; outX < end2; ++outX) { \
 953			uint32_t* pixel = &renderer->row[outX]; \
 954			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
 955		} \
 956	} \
 957	\
 958	tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
 959	tileData >>= 8 * shift; \
 960	for (; outX < end; ++outX) { \
 961		uint32_t* pixel = &renderer->row[outX]; \
 962		BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
 963	}
 964
 965#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
 966	/* TODO: hflip */ \
 967	charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
 968	outX = renderer->end - 8 + end; \
 969	int end2 = 4 - end; \
 970	if (end2 > 0) { \
 971		tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 972		for (; outX < renderer->end - end2; ++outX) { \
 973			uint32_t* pixel = &renderer->row[outX]; \
 974			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
 975		} \
 976		++charBase; \
 977	} \
 978	\
 979	tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 980	for (; outX < renderer->end; ++outX) { \
 981		uint32_t* pixel = &renderer->row[outX]; \
 982		BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
 983	}
 984
 985#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
 986	for (; tileX < tileEnd; ++tileX) { \
 987		BACKGROUND_TEXT_SELECT_CHARACTER; \
 988		charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
 989		if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 990			tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 991			if (tileData) { \
 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					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
 999					++pixel; \
1000			} else { \
1001				pixel += 4; \
1002			} \
1003			tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
1004			if (tileData) { \
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					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1012					++pixel; \
1013			} else { \
1014				pixel += 4; \
1015			} \
1016		} else { \
1017			uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
1018			if (tileData) { \
1019				pixel += 3; \
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				--pixel; \
1026				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1027			} \
1028			pixel += 4; \
1029			tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
1030			if (tileData) { \
1031				pixel += 3; \
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				--pixel; \
1038				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1039			} \
1040			pixel += 4; \
1041		} \
1042	}
1043
1044#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
1045	for (; tileX < tileEnd; ++tileX) { \
1046		BACKGROUND_TEXT_SELECT_CHARACTER; \
1047		charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \
1048		tileData = carryData; \
1049		for (x = 0; x < 8; ++x) { \
1050			if (!mosaicWait) { \
1051				if (x >= 4) { \
1052					tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
1053					if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1054						tileData >>= (x - 4) * 8; \
1055					} else { \
1056						tileData >>= (7 - x) * 8; \
1057					} \
1058				} else { \
1059					tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
1060					if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1061						tileData >>= x * 8; \
1062					} else { \
1063						tileData >>= (3 - x) * 8; \
1064					} \
1065				} \
1066				tileData &= 0xFF; \
1067				carryData = tileData; \
1068				mosaicWait = mosaicH; \
1069			} \
1070			tileData |= tileData << 8; \
1071			--mosaicWait; \
1072			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1073			++pixel; \
1074		} \
1075	}
1076
1077#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
1078	if (inX & 0x7) { \
1079		int mod8 = inX & 0x7; \
1080		BACKGROUND_TEXT_SELECT_CHARACTER; \
1081		\
1082		int end = outX + 0x8 - mod8; \
1083		if (end > renderer->end) { \
1084			/* TODO: ensure tiles are properly aligned from this*/ \
1085			end = renderer->end; \
1086		} \
1087		DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
1088	} \
1089	if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) { \
1090		tileX = tileEnd; \
1091		int pixelData; \
1092		int mod8 = (inX + renderer->end - renderer->start) & 0x7; \
1093		BACKGROUND_TEXT_SELECT_CHARACTER; \
1094		\
1095		int end = 0x8 - mod8; \
1096		UNUSED(end); \
1097		DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
1098		\
1099		tileX = (inX & 0x7) != 0; \
1100		outX = renderer->start + tileX * 8 - (inX & 0x7); \
1101	} \
1102	\
1103	uint32_t* pixel = &renderer->row[outX]; \
1104	if (background->mosaic) { \
1105		int mosaicH = renderer->mosaic.bgH + 1; \
1106		int x; \
1107		int mosaicWait = outX % mosaicH; \
1108		int carryData = 0; \
1109		paletteData = 0; /* Quiets compiler warning */ \
1110		DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
1111		return; \
1112	} \
1113	\
1114	DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN)
1115
1116static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
1117	int inX = renderer->start + background->x;
1118	if (background->mosaic) {
1119		int mosaicV = renderer->mosaic.bgV + 1;
1120		y -= y % mosaicV;
1121	}
1122	int inY = y + background->y;
1123	uint16_t mapData;
1124
1125	unsigned yBase = inY & 0xF8;
1126	if (background->size == 2) {
1127		yBase += inY & 0x100;
1128	} else if (background->size == 3) {
1129		yBase += (inY & 0x100) << 1;
1130	}
1131	yBase = (background->screenBase >> 1) + (yBase << 2);
1132
1133	int localX;
1134	int localY;
1135
1136	unsigned xBase;
1137
1138	int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
1139	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
1140	flags |= FLAG_TARGET_2 * background->target2;
1141
1142	uint32_t screenBase;
1143	uint32_t charBase;
1144	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1145	color_t* mainPalette = renderer->normalPalette;
1146	if (variant) {
1147		mainPalette = renderer->variantPalette;
1148	}
1149	color_t* palette = mainPalette;
1150	PREPARE_OBJWIN;
1151
1152	int outX = renderer->start;
1153
1154	uint32_t tileData;
1155	uint32_t current;
1156	int pixelData;
1157	int paletteData;
1158	int tileX = 0;
1159	int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
1160
1161	if (!objwinSlowPath) {
1162		if (!(flags & FLAG_TARGET_2)) {
1163			if (!background->multipalette) {
1164				DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
1165			} else {
1166				DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
1167			}
1168		} else {
1169			if (!background->multipalette) {
1170				DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
1171			} else {
1172				DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
1173			}
1174		}
1175	} else {
1176		if (!(flags & FLAG_TARGET_2)) {
1177			if (!background->multipalette) {
1178				DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
1179			} else {
1180				DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
1181			}
1182		} else {
1183			if (!background->multipalette) {
1184				DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
1185			} else {
1186				DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
1187			}
1188		}
1189	}
1190}
1191
1192#define BACKGROUND_BITMAP_INIT \
1193	UNUSED(unused); \
1194	int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1195	int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1196	int32_t localX; \
1197	int32_t localY; \
1198	\
1199	int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
1200	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1201	flags |= FLAG_TARGET_2 * background->target2; \
1202	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1203	color_t* palette = renderer->normalPalette; \
1204	if (variant) { \
1205		palette = renderer->variantPalette; \
1206	} \
1207	PREPARE_OBJWIN;
1208
1209#define BACKGROUND_BITMAP_ITERATE(W, H) \
1210	x += background->dx; \
1211	y += background->dy; \
1212	\
1213	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1214		continue; \
1215	} else { \
1216		localX = x; \
1217		localY = y; \
1218	}
1219
1220static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1221	int sizeAdjusted = 0x8000 << background->size;
1222
1223	BACKGROUND_BITMAP_INIT;
1224
1225	uint32_t screenBase = background->screenBase;
1226	uint32_t charBase = background->charBase;
1227	uint8_t mapData;
1228	uint8_t tileData;
1229
1230	int outX;
1231	uint32_t* pixel;
1232	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1233		x += background->dx;
1234		y += background->dy;
1235
1236		if (background->overflow) {
1237			localX = x & (sizeAdjusted - 1);
1238			localY = y & (sizeAdjusted - 1);
1239		} else if ((x | y) & ~(sizeAdjusted - 1)) {
1240			continue;
1241		} else {
1242			localX = x;
1243			localY = y;
1244		}
1245		mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1246		tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1247
1248		uint32_t current = *pixel;
1249		if (tileData && IS_WRITABLE(current)) {
1250			if (!objwinSlowPath) {
1251				_compositeBlendNoObjwin(renderer, pixel, palette[tileData] | flags, current);
1252			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1253				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1254				_compositeBlendObjwin(renderer, pixel, currentPalette[tileData] | flags, current);
1255			}
1256		}
1257	}
1258}
1259
1260static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1261	BACKGROUND_BITMAP_INIT;
1262
1263	uint32_t color;
1264
1265	int outX;
1266	uint32_t* pixel;
1267	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1268		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1269
1270		color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1271#ifndef COLOR_16_BIT
1272		unsigned color32;
1273		color32 = 0;
1274		color32 |= (color << 3) & 0xF8;
1275		color32 |= (color << 6) & 0xF800;
1276		color32 |= (color << 9) & 0xF80000;
1277		color = color32;
1278#endif
1279
1280		uint32_t current = *pixel;
1281		if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1282			if (!variant) {
1283				_compositeBlendObjwin(renderer, pixel, color | flags, current);
1284			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1285				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1286			} else if (renderer->blendEffect == BLEND_DARKEN) {
1287				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1288			}
1289		}
1290	}
1291}
1292
1293static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1294	BACKGROUND_BITMAP_INIT;
1295
1296	uint16_t color;
1297	uint32_t offset = 0;
1298	if (renderer->dispcnt.frameSelect) {
1299		offset = 0xA000;
1300	}
1301
1302	int outX;
1303	uint32_t* pixel;
1304	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1305		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1306
1307		color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1308
1309		uint32_t current = *pixel;
1310		if (color && IS_WRITABLE(current)) {
1311			if (!objwinSlowPath) {
1312				_compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
1313			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1314				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1315				_compositeBlendObjwin(renderer, pixel, currentPalette[color] | flags, current);
1316			}
1317		}
1318	}
1319}
1320
1321static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1322	BACKGROUND_BITMAP_INIT;
1323
1324	uint32_t color;
1325	uint32_t offset = 0;
1326	if (renderer->dispcnt.frameSelect) {
1327		offset = 0xA000;
1328	}
1329
1330	int outX;
1331	uint32_t* pixel;
1332	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1333		BACKGROUND_BITMAP_ITERATE(160, 128);
1334
1335		color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1336#ifndef COLOR_16_BIT
1337		unsigned color32 = 0;
1338		color32 |= (color << 9) & 0xF80000;
1339		color32 |= (color << 3) & 0xF8;
1340		color32 |= (color << 6) & 0xF800;
1341		color = color32;
1342#endif
1343
1344		uint32_t current = *pixel;
1345		if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1346			if (!variant) {
1347				_compositeBlendObjwin(renderer, pixel, color | flags, current);
1348			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1349				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1350			} else if (renderer->blendEffect == BLEND_DARKEN) {
1351				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1352			}
1353		}
1354	}
1355}
1356
1357#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1358	SPRITE_YBASE_ ## DEPTH(inY); \
1359	for (; outX < condition; ++outX, inX += xOffset) { \
1360		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1361			continue; \
1362		} \
1363		SPRITE_XBASE_ ## DEPTH(inX); \
1364		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1365	}
1366
1367#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1368	SPRITE_YBASE_ ## DEPTH(inY); \
1369	if (outX % mosaicH) { \
1370		inX += (mosaicH - (outX % mosaicH)) * xOffset; \
1371		outX += mosaicH - (outX % mosaicH); \
1372	} \
1373	for (; outX < condition; ++outX, inX += xOffset) { \
1374		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1375			continue; \
1376		} \
1377		int localX = inX - xOffset * (outX % mosaicH); \
1378		SPRITE_XBASE_ ## DEPTH(localX); \
1379		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1380	}
1381
1382#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1383	int outX; \
1384	for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1385		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1386			continue; \
1387		} \
1388		int inX = outX - x; \
1389		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1390		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1391		\
1392		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1393			continue; \
1394		} \
1395		\
1396		SPRITE_YBASE_ ## DEPTH(localY); \
1397		SPRITE_XBASE_ ## DEPTH(localX); \
1398		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1399	}
1400
1401#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1402#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1403
1404#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1405	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1406	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1407	if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1408		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1409	}
1410
1411#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1412	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1413	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1414	if (tileData) { \
1415		renderer->row[outX] |= FLAG_OBJWIN; \
1416	}
1417
1418#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1419#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1420
1421#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1422	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1423	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1424	if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1425		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1426	}
1427
1428#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1429	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1430	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1431	if (tileData) { \
1432		renderer->row[outX] |= FLAG_OBJWIN; \
1433	}
1434
1435static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1436	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1437	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1438	int start = renderer->start;
1439	int end = renderer->end;
1440	uint32_t flags = sprite->priority << OFFSET_PRIORITY;
1441	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1442	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1443	int x = sprite->x;
1444	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1445	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1446	if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1447		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1448		variant = 0;
1449	}
1450	color_t* palette = &renderer->normalPalette[0x100];
1451	if (variant) {
1452		palette = &renderer->variantPalette[0x100];
1453	}
1454
1455	int outX = x >= start ? x : start;
1456	int condition = x + width;
1457	int mosaicH = 1;
1458	if (sprite->mosaic) {
1459		mosaicH = renderer->mosaic.objH + 1;
1460		if (condition % mosaicH) {
1461			condition += mosaicH - (condition % mosaicH);
1462		}
1463	}
1464	int inY = y - sprite->y;
1465	if (sprite->y + height - 256 >= 0) {
1466		inY += 256;
1467	}
1468	if (sprite->vflip) {
1469		inY = height - inY - 1;
1470	}
1471	if (end < condition) {
1472		condition = end;
1473	}
1474	int inX = outX - x;
1475	int xOffset = 1;
1476	if (sprite->hflip) {
1477		inX = width - inX - 1;
1478		xOffset = -1;
1479	}
1480	if (!sprite->multipalette) {
1481		palette = &palette[sprite->palette << 4];
1482		if (flags & FLAG_OBJWIN) {
1483			SPRITE_NORMAL_LOOP(16, OBJWIN);
1484		} else if (sprite->mosaic) {
1485			SPRITE_MOSAIC_LOOP(16, NORMAL);
1486		} else {
1487			SPRITE_NORMAL_LOOP(16, NORMAL);
1488		}
1489	} else {
1490		if (flags & FLAG_OBJWIN) {
1491			SPRITE_NORMAL_LOOP(256, OBJWIN);
1492		} else if (sprite->mosaic) {
1493			SPRITE_MOSAIC_LOOP(256, NORMAL);
1494		} else {
1495			SPRITE_NORMAL_LOOP(256, NORMAL);
1496		}
1497	}
1498	return 1;
1499}
1500
1501static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1502	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1503	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1504	int totalWidth = width << sprite->doublesize;
1505	int totalHeight = height << sprite->doublesize;
1506	int start = renderer->start;
1507	int end = renderer->end;
1508	uint32_t flags = sprite->priority << OFFSET_PRIORITY;
1509	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1510	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1511	int x = sprite->x;
1512	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1513	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1514	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1515	if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1516		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1517		variant = 0;
1518	}
1519	color_t* palette = &renderer->normalPalette[0x100];
1520	if (variant) {
1521		palette = &renderer->variantPalette[0x100];
1522	}
1523	int inY = y - sprite->y;
1524	if (inY < 0) {
1525		inY += 256;
1526	}
1527	if (!sprite->multipalette) {
1528		palette = &palette[sprite->palette << 4];
1529		if (flags & FLAG_OBJWIN) {
1530			SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1531		} else {
1532			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1533		}
1534	} else {
1535		if (flags & FLAG_OBJWIN) {
1536			SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1537		} else {
1538			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1539		}
1540	}
1541	return 1;
1542}
1543
1544static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1545	int x;
1546	uint32_t* pixel = renderer->row;
1547	uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
1548	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1549		uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1550		uint32_t current = *pixel;
1551		if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1552			_compositeBlendObjwin(renderer, pixel, color | flags, current);
1553		}
1554	}
1555}
1556
1557static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1558	int i;
1559	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1560		for (i = 0; i < 512; ++i) {
1561			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1562		}
1563	} else if (renderer->blendEffect == BLEND_DARKEN) {
1564		for (i = 0; i < 512; ++i) {
1565			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1566		}
1567	} else {
1568		for (i = 0; i < 512; ++i) {
1569			renderer->variantPalette[i] = renderer->normalPalette[i];
1570		}
1571	}
1572}
1573
1574static inline unsigned _brighten(unsigned color, int y) {
1575	unsigned c = 0;
1576	unsigned a;
1577#ifdef COLOR_16_BIT
1578	a = color & 0x1F;
1579	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1580
1581#ifdef COLOR_5_6_5
1582	a = color & 0x7C0;
1583	c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1584
1585	a = color & 0xF800;
1586	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1587#else
1588	a = color & 0x3E0;
1589	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1590
1591	a = color & 0x7C00;
1592	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1593#endif
1594#else
1595	a = color & 0xF8;
1596	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1597
1598	a = color & 0xF800;
1599	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1600
1601	a = color & 0xF80000;
1602	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1603#endif
1604	return c;
1605}
1606
1607static inline unsigned _darken(unsigned color, int y) {
1608	unsigned c = 0;
1609	unsigned a;
1610#ifdef COLOR_16_BIT
1611	a = color & 0x1F;
1612	c |= (a - (a * y) / 16) & 0x1F;
1613
1614#ifdef COLOR_5_6_5
1615	a = color & 0x7C0;
1616	c |= (a - (a * y) / 16) & 0x7C0;
1617
1618	a = color & 0xF800;
1619	c |= (a - (a * y) / 16) & 0xF800;
1620#else
1621	a = color & 0x3E0;
1622	c |= (a - (a * y) / 16) & 0x3E0;
1623
1624	a = color & 0x7C00;
1625	c |= (a - (a * y) / 16) & 0x7C00;
1626#endif
1627#else
1628	a = color & 0xF8;
1629	c |= (a - (a * y) / 16) & 0xF8;
1630
1631	a = color & 0xF800;
1632	c |= (a - (a * y) / 16) & 0xF800;
1633
1634	a = color & 0xF80000;
1635	c |= (a - (a * y) / 16) & 0xF80000;
1636#endif
1637	return c;
1638}
1639
1640static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1641	unsigned c = 0;
1642	unsigned a, b;
1643#ifdef COLOR_16_BIT
1644#ifdef COLOR_5_6_5
1645	a = colorA & 0xF81F;
1646	b = colorB & 0xF81F;
1647	a |= (colorA & 0x7C0) << 16;
1648	b |= (colorB & 0x7C0) << 16;
1649	c = ((a * weightA + b * weightB) / 16);
1650	if (c & 0x08000000) {
1651		c = (c & ~0x0FC00000) | 0x07C00000;
1652	}
1653	if (c & 0x0020) {
1654		c = (c & ~0x003F) | 0x001F;
1655	}
1656	if (c & 0x10000) {
1657		c = (c & ~0x1F800) | 0xF800;
1658	}
1659	c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
1660#else
1661	a = colorA & 0x7C1F;
1662	b = colorB & 0x7C1F;
1663	a |= (colorA & 0x3E0) << 16;
1664	b |= (colorB & 0x3E0) << 16;
1665	c = ((a * weightA + b * weightB) / 16);
1666	if (c & 0x04000000) {
1667		c = (c & ~0x07E00000) | 0x03E00000;
1668	}
1669	if (c & 0x0020) {
1670		c = (c & ~0x003F) | 0x001F;
1671	}
1672	if (c & 0x10000) {
1673		c = (c & ~0x1F800) | 0xF800;
1674	}
1675	c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
1676#endif
1677#else
1678	a = colorA & 0xF8;
1679	b = colorB & 0xF8;
1680	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1681	if (c & 0x00000100) {
1682		c = 0x000000F8;
1683	}
1684
1685	a = colorA & 0xF800;
1686	b = colorB & 0xF800;
1687	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1688	if (c & 0x00010000) {
1689		c = (c & 0x000000F8) | 0x0000F800;
1690	}
1691
1692	a = colorA & 0xF80000;
1693	b = colorB & 0xF80000;
1694	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1695	if (c & 0x01000000) {
1696		c = (c & 0x0000F8F8) | 0x00F80000;
1697	}
1698#endif
1699	return c;
1700}