all repos — mgba @ 66ee98513fa240be9b0bf0b98b4a1f697fcf0c6c

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