all repos — mgba @ 6eafbc6331623f3c246f7ce6b5fef4c7e6189794

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 void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
  35static void _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		for (; x < softwareRenderer->windows[w].endX; ++x) {
 376			softwareRenderer->row[x] = backdrop;
 377		}
 378	}
 379
 380	_drawScanline(softwareRenderer, y);
 381
 382	if (softwareRenderer->target2Bd) {
 383		x = 0;
 384		for (w = 0; w < softwareRenderer->nWindows; ++w) {
 385		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
 386			if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
 387				backdrop |= softwareRenderer->normalPalette[0];
 388			} else {
 389				backdrop |= softwareRenderer->variantPalette[0];
 390			}
 391			for (; x < softwareRenderer->windows[w].endX; ++x) {
 392				uint32_t color = softwareRenderer->row[x];
 393				if (color & FLAG_TARGET_1 && !(color & FLAG_FINALIZED)) {
 394					softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
 395				}
 396			}
 397		}
 398	}
 399
 400#ifdef COLOR_16_BIT
 401	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 402		uint32_t c = softwareRenderer->row[x];
 403#ifdef COLOR_5_6_5
 404		c = ((c & 0x001F) << 11) | ((c & 0x03E0) << 1) | ((c & 0x7C00) >> 10);
 405#endif
 406		row[x] = c;
 407	}
 408#else
 409	memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
 410#endif
 411}
 412
 413static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
 414	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 415
 416	softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
 417	softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
 418	softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
 419	softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
 420}
 421
 422static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
 423	renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
 424	renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
 425	renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
 426	renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
 427}
 428
 429static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 430	(void)(renderer);
 431	union GBARegisterBGCNT reg = { .packed = value };
 432	bg->priority = reg.priority;
 433	bg->charBase = reg.charBase << 14;
 434	bg->mosaic = reg.mosaic;
 435	bg->multipalette = reg.multipalette;
 436	bg->screenBase = reg.screenBase << 11;
 437	bg->overflow = reg.overflow;
 438	bg->size = reg.size;
 439}
 440
 441static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 442	bg->dx = value;
 443}
 444
 445static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 446	bg->dmx = value;
 447}
 448
 449static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 450	bg->dy = value;
 451}
 452
 453static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 454	bg->dmy = value;
 455}
 456
 457static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 458	bg->refx = (bg->refx & 0xFFFF0000) | value;
 459	bg->sx = bg->refx;
 460}
 461
 462static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 463	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
 464	bg->refx <<= 4;
 465	bg->refx >>= 4;
 466	bg->sx = bg->refx;
 467}
 468
 469static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 470	bg->refy = (bg->refy & 0xFFFF0000) | value;
 471	bg->sy = bg->refy;
 472}
 473
 474static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 475	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
 476	bg->refy <<= 4;
 477	bg->refy >>= 4;
 478	bg->sy = bg->refy;
 479}
 480
 481static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
 482	union {
 483		struct {
 484			unsigned target1Bg0 : 1;
 485			unsigned target1Bg1 : 1;
 486			unsigned target1Bg2 : 1;
 487			unsigned target1Bg3 : 1;
 488			unsigned target1Obj : 1;
 489			unsigned target1Bd : 1;
 490			enum BlendEffect effect : 2;
 491			unsigned target2Bg0 : 1;
 492			unsigned target2Bg1 : 1;
 493			unsigned target2Bg2 : 1;
 494			unsigned target2Bg3 : 1;
 495			unsigned target2Obj : 1;
 496			unsigned target2Bd : 1;
 497		};
 498		uint16_t packed;
 499	} bldcnt = { .packed = value };
 500
 501	enum BlendEffect oldEffect = renderer->blendEffect;
 502
 503	renderer->bg[0].target1 = bldcnt.target1Bg0;
 504	renderer->bg[1].target1 = bldcnt.target1Bg1;
 505	renderer->bg[2].target1 = bldcnt.target1Bg2;
 506	renderer->bg[3].target1 = bldcnt.target1Bg3;
 507	renderer->bg[0].target2 = bldcnt.target2Bg0;
 508	renderer->bg[1].target2 = bldcnt.target2Bg1;
 509	renderer->bg[2].target2 = bldcnt.target2Bg2;
 510	renderer->bg[3].target2 = bldcnt.target2Bg3;
 511
 512	renderer->blendEffect = bldcnt.effect;
 513	renderer->target1Obj = bldcnt.target1Obj;
 514	renderer->target1Bd = bldcnt.target1Bd;
 515	renderer->target2Obj = bldcnt.target2Obj;
 516	renderer->target2Bd = bldcnt.target2Bd;
 517
 518	if (oldEffect != renderer->blendEffect) {
 519		_updatePalettes(renderer);
 520	}
 521}
 522
 523#define TEST_LAYER_ENABLED(X) \
 524	(renderer->bg[X].enabled && \
 525	(renderer->currentWindow.bg ## X ## Enable || \
 526	(renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
 527	renderer->bg[X].priority == priority)
 528
 529static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
 530	int w;
 531	renderer->end = 0;
 532	if (renderer->dispcnt.objEnable) {
 533		for (w = 0; w < renderer->nWindows; ++w) {
 534			renderer->start = renderer->end;
 535			renderer->end = renderer->windows[w].endX;
 536			renderer->currentWindow = renderer->windows[w].control;
 537			if (!renderer->currentWindow.objEnable) {
 538				continue;
 539			}
 540			int i;
 541			for (i = 0; i < 128; ++i) {
 542				struct GBAObj* sprite = &renderer->d.oam->obj[i];
 543				if (sprite->transformed) {
 544					_preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
 545				} else if (!sprite->disable) {
 546					_preprocessSprite(renderer, sprite, y);
 547				}
 548			}
 549		}
 550	}
 551
 552	int priority;
 553	for (priority = 0; priority < 4; ++priority) {
 554		_postprocessSprite(renderer, priority);
 555		renderer->end = 0;
 556		for (w = 0; w < renderer->nWindows; ++w) {
 557			renderer->start = renderer->end;
 558			renderer->end = renderer->windows[w].endX;
 559			renderer->currentWindow = renderer->windows[w].control;
 560			if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
 561				_drawBackgroundMode0(renderer, &renderer->bg[0], y);
 562			}
 563			if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
 564				_drawBackgroundMode0(renderer, &renderer->bg[1], y);
 565			}
 566			if (TEST_LAYER_ENABLED(2)) {
 567				switch (renderer->dispcnt.mode) {
 568				case 0:
 569					_drawBackgroundMode0(renderer, &renderer->bg[2], y);
 570					break;
 571				case 1:
 572				case 2:
 573					_drawBackgroundMode2(renderer, &renderer->bg[2], y);
 574					break;
 575				case 3:
 576					_drawBackgroundMode3(renderer, &renderer->bg[2], y);
 577					break;
 578				case 4:
 579					_drawBackgroundMode4(renderer, &renderer->bg[2], y);
 580					break;
 581				case 5:
 582					_drawBackgroundMode5(renderer, &renderer->bg[2], y);
 583					break;
 584				}
 585			}
 586			if (TEST_LAYER_ENABLED(3)) {
 587				switch (renderer->dispcnt.mode) {
 588				case 0:
 589					_drawBackgroundMode0(renderer, &renderer->bg[3], y);
 590					break;
 591				case 2:
 592					_drawBackgroundMode2(renderer, &renderer->bg[3], y);
 593					break;
 594				}
 595			}
 596		}
 597	}
 598	renderer->bg[2].sx += renderer->bg[2].dmx;
 599	renderer->bg[2].sy += renderer->bg[2].dmy;
 600	renderer->bg[3].sx += renderer->bg[3].dmx;
 601	renderer->bg[3].sy += renderer->bg[3].dmy;
 602}
 603
 604static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color, uint32_t current) {
 605	// We stash the priority on the top bits so we can do a one-operator comparison
 606	// The lower the number, the higher the priority, and sprites take precendence over backgrounds
 607	// We want to do special processing if the color pixel is target 1, however
 608	if ((color & FLAG_ORDER_MASK) < (current & FLAG_ORDER_MASK)) {
 609		if (current & FLAG_UNWRITTEN) {
 610			renderer->row[offset] = color | (current & FLAG_OBJWIN);
 611		} else if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
 612			renderer->row[offset] = color | FLAG_FINALIZED;
 613		} else {
 614			renderer->row[offset] = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
 615		}
 616	} else {
 617		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 618			renderer->row[offset] = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
 619		} else {
 620			renderer->row[offset] = current | FLAG_FINALIZED;
 621		}
 622	}
 623}
 624
 625#define BACKGROUND_DRAW_PIXEL_16 \
 626	pixelData = tileData & 0xF; \
 627	current = renderer->row[outX]; \
 628	if (pixelData && !(current & FLAG_FINALIZED)) { \
 629		if (!objwinSlowPath) { \
 630			_composite(renderer, outX, palette[pixelData | paletteData] | flags, current); \
 631		} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
 632			color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette; \
 633			_composite(renderer, outX, currentPalette[pixelData | paletteData] | flags, current); \
 634		} \
 635	} \
 636	tileData >>= 4;
 637
 638#define BACKGROUND_DRAW_PIXEL_256 \
 639	pixelData = tileData & 0xFF; \
 640	current = renderer->row[outX]; \
 641	if (pixelData && !(current & FLAG_FINALIZED)) { \
 642		if (!objwinSlowPath) { \
 643			_composite(renderer, outX, palette[pixelData] | flags, current); \
 644		} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
 645			color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette; \
 646			_composite(renderer, outX, currentPalette[pixelData] | flags, current); \
 647		} \
 648	} \
 649	tileData >>= 8;
 650
 651#define BACKGROUND_TEXT_SELECT_CHARACTER \
 652	localX = tileX * 8 + inX; \
 653	xBase = localX & 0xF8; \
 654	if (background->size & 1) { \
 655		xBase += (localX & 0x100) << 5; \
 656	} \
 657	screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
 658	mapData.packed = renderer->d.vram[screenBase]; \
 659	if (!mapData.vflip) { \
 660		localY = inY & 0x7; \
 661	} else { \
 662		localY = 7 - (inY & 0x7); \
 663	}
 664
 665#define PREPARE_OBJWIN \
 666	int objwinSlowPath = renderer->dispcnt.objwinEnable; \
 667	int objwinOnly = 0; \
 668	int objwinForceEnable = 0; \
 669	color_t* objwinPalette; \
 670	if (objwinSlowPath) { \
 671		if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
 672			objwinPalette = renderer->variantPalette; \
 673		} else { \
 674			objwinPalette = renderer->normalPalette; \
 675		} \
 676		switch (background->index) { \
 677		case 0: \
 678			objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \
 679			objwinOnly = !renderer->objwin.bg0Enable; \
 680			break; \
 681		case 1: \
 682			objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \
 683			objwinOnly = !renderer->objwin.bg1Enable; \
 684			break; \
 685		case 2: \
 686			objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \
 687			objwinOnly = !renderer->objwin.bg2Enable; \
 688			break; \
 689		case 3: \
 690			objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \
 691			objwinOnly = !renderer->objwin.bg3Enable; \
 692			break; \
 693		} \
 694	}
 695
 696static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
 697	int inX = renderer->start + background->x;
 698	if (background->mosaic) {
 699		int mosaicV = renderer->mosaic.bgV + 1;
 700		y -= y % mosaicV;
 701	}
 702	int inY = y + background->y;
 703	union GBATextMapData mapData;
 704
 705	unsigned yBase = inY & 0xF8;
 706	if (background->size == 2) {
 707		yBase += inY & 0x100;
 708	} else if (background->size == 3) {
 709		yBase += (inY & 0x100) << 1;
 710	}
 711
 712	int localX;
 713	int localY;
 714
 715	unsigned xBase;
 716
 717	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
 718	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
 719	flags |= FLAG_TARGET_2 * background->target2;
 720
 721	uint32_t screenBase;
 722	uint32_t charBase;
 723	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
 724	color_t* palette = renderer->normalPalette;
 725	if (variant) {
 726		palette = renderer->variantPalette;
 727	}
 728	PREPARE_OBJWIN;
 729
 730	int outX = renderer->start;
 731
 732	uint32_t tileData;
 733	uint32_t current;
 734	int pixelData;
 735	int paletteData;
 736	int tileX = 0;
 737	int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
 738
 739	if (inX & 0x7) {
 740		int mod8 = inX & 0x7;
 741		BACKGROUND_TEXT_SELECT_CHARACTER;
 742
 743		int end = outX + 0x8 - mod8;
 744		if (!background->multipalette) {
 745			paletteData = mapData.palette << 4;
 746			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 747			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 748			if (!mapData.hflip) {
 749				tileData >>= 4 * mod8;
 750				for (; outX < end; ++outX) {
 751					BACKGROUND_DRAW_PIXEL_16;
 752				}
 753			} else {
 754				for (outX = end - 1; outX >= renderer->start; --outX) {
 755					BACKGROUND_DRAW_PIXEL_16;
 756				}
 757			}
 758		} else {
 759			// TODO: hflip
 760			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 761			int end2 = end - 4;
 762			int shift = inX & 0x3;
 763			if (end2 > 0) {
 764				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 765				tileData >>= 8 * shift;
 766				shift = 0;
 767				for (; outX < end2; ++outX) {
 768					BACKGROUND_DRAW_PIXEL_256;
 769				}
 770			}
 771
 772			tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 773			tileData >>= 8 * shift;
 774			for (; outX < end; ++outX) {
 775				BACKGROUND_DRAW_PIXEL_256;
 776			}
 777		}
 778	}
 779	if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) {
 780		tileX = tileEnd;
 781		int pixelData, paletteData;
 782		int mod8 = (inX + renderer->end - renderer->start) & 0x7;
 783		BACKGROUND_TEXT_SELECT_CHARACTER;
 784
 785		int end = 0x8 - mod8;
 786		if (!background->multipalette) {
 787			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 788			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 789			paletteData = mapData.palette << 4;
 790			if (!mapData.hflip) {
 791				outX = renderer->end - mod8;
 792				if (outX < 0) {
 793					tileData >>= 4 * -outX;
 794					outX = 0;
 795				}
 796				for (; outX < renderer->end; ++outX) {
 797					BACKGROUND_DRAW_PIXEL_16;
 798				}
 799			} else {
 800				tileData >>= 4 * (0x8 - mod8);
 801				int end2 = renderer->end - 8;
 802				if (end2 < -1) {
 803					end2 = -1;
 804				}
 805				for (outX = renderer->end - 1; outX > end2; --outX) {
 806					BACKGROUND_DRAW_PIXEL_16;
 807				}
 808			}
 809		} else {
 810			// TODO: hflip
 811			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 812			outX = renderer->end - 8 + end;
 813			int end2 = 4 - end;
 814			if (end2 > 0) {
 815				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 816				for (; outX < renderer->end - end2; ++outX) {
 817					BACKGROUND_DRAW_PIXEL_256;
 818				}
 819				++charBase;
 820			}
 821
 822			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 823			for (; outX < renderer->end; ++outX) {
 824				BACKGROUND_DRAW_PIXEL_256;
 825			}
 826		}
 827
 828		tileX = (inX & 0x7) != 0;
 829		outX = renderer->start + tileX * 8 - (inX & 0x7);
 830	}
 831
 832	if (background->mosaic) {
 833		int mosaicH = renderer->mosaic.bgH + 1;
 834		int x;
 835		int mosaicWait = outX % mosaicH;
 836		int carryData = 0;
 837
 838		if (!background->multipalette) {
 839			for (; tileX < tileEnd; ++tileX) {
 840				BACKGROUND_TEXT_SELECT_CHARACTER;
 841				charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 842				tileData = carryData;
 843				for (x = 0; x < 8; ++x) {
 844					if (!mosaicWait) {
 845						paletteData = mapData.palette << 4;
 846						tileData = ((uint32_t*)renderer->d.vram)[charBase];
 847						if (!mapData.hflip) {
 848							tileData >>= x * 4;
 849						} else {
 850							tileData >>= (7 - x) * 4;
 851						}
 852						tileData &= 0xF;
 853						tileData |= tileData << 4;
 854						tileData |= tileData << 8;
 855						tileData |= tileData << 12;
 856						tileData |= tileData << 16;
 857						tileData |= tileData << 20;
 858						tileData |= tileData << 24;
 859						tileData |= tileData << 28;
 860						carryData = tileData;
 861						mosaicWait = mosaicH;
 862					}
 863					--mosaicWait;
 864					BACKGROUND_DRAW_PIXEL_16;
 865					++outX;
 866				}
 867			}
 868		} else {
 869			// TODO: 256-color mosaic
 870		}
 871		return;
 872	}
 873
 874	if (!background->multipalette) {
 875		for (; tileX < tileEnd; ++tileX) {
 876			BACKGROUND_TEXT_SELECT_CHARACTER;
 877			paletteData = mapData.palette << 4;
 878			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 879			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 880			if (tileData) {
 881				if (!mapData.hflip) {
 882					BACKGROUND_DRAW_PIXEL_16;
 883					++outX;
 884					BACKGROUND_DRAW_PIXEL_16;
 885					++outX;
 886					BACKGROUND_DRAW_PIXEL_16;
 887					++outX;
 888					BACKGROUND_DRAW_PIXEL_16;
 889					++outX;
 890					BACKGROUND_DRAW_PIXEL_16;
 891					++outX;
 892					BACKGROUND_DRAW_PIXEL_16;
 893					++outX;
 894					BACKGROUND_DRAW_PIXEL_16;
 895					++outX;
 896					BACKGROUND_DRAW_PIXEL_16;
 897					++outX;
 898				} else {
 899					outX += 7;
 900					BACKGROUND_DRAW_PIXEL_16;
 901					--outX;
 902					BACKGROUND_DRAW_PIXEL_16;
 903					--outX;
 904					BACKGROUND_DRAW_PIXEL_16;
 905					--outX;
 906					BACKGROUND_DRAW_PIXEL_16;
 907					--outX;
 908					BACKGROUND_DRAW_PIXEL_16;
 909					--outX;
 910					BACKGROUND_DRAW_PIXEL_16;
 911					--outX;
 912					BACKGROUND_DRAW_PIXEL_16;
 913					--outX;
 914					BACKGROUND_DRAW_PIXEL_16;
 915					outX += 8;
 916				}
 917			} else {
 918				outX += 8;
 919			}
 920		}
 921	} else {
 922		for (; tileX < tileEnd; ++tileX) {
 923			BACKGROUND_TEXT_SELECT_CHARACTER;
 924			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 925			if (!mapData.hflip) {
 926				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 927				if (tileData) {
 928						BACKGROUND_DRAW_PIXEL_256;
 929						++outX;
 930						BACKGROUND_DRAW_PIXEL_256;
 931						++outX;
 932						BACKGROUND_DRAW_PIXEL_256;
 933						++outX;
 934						BACKGROUND_DRAW_PIXEL_256;
 935						++outX;
 936				} else {
 937					outX += 4;
 938				}
 939				tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 940				if (tileData) {
 941						BACKGROUND_DRAW_PIXEL_256;
 942						++outX;
 943						BACKGROUND_DRAW_PIXEL_256;
 944						++outX;
 945						BACKGROUND_DRAW_PIXEL_256;
 946						++outX;
 947						BACKGROUND_DRAW_PIXEL_256;
 948						++outX;
 949				} else {
 950					outX += 4;
 951				}
 952			} else {
 953				uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 954				if (tileData) {
 955					outX += 3;
 956					BACKGROUND_DRAW_PIXEL_256;
 957					--outX;
 958					BACKGROUND_DRAW_PIXEL_256;
 959					--outX;
 960					BACKGROUND_DRAW_PIXEL_256;
 961					--outX;
 962					BACKGROUND_DRAW_PIXEL_256;
 963					outX += 4;
 964				} else {
 965					outX += 4;
 966				}
 967				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 968				if (tileData) {
 969					outX += 3;
 970					BACKGROUND_DRAW_PIXEL_256;
 971					--outX;
 972					BACKGROUND_DRAW_PIXEL_256;
 973					--outX;
 974					BACKGROUND_DRAW_PIXEL_256;
 975					--outX;
 976					BACKGROUND_DRAW_PIXEL_256;
 977					outX += 4;
 978				} else {
 979					outX += 4;
 980				}
 981			}
 982		}
 983	}
 984}
 985
 986#define BACKGROUND_BITMAP_INIT \
 987	(void)(unused); \
 988	int32_t x = background->sx - background->dx; \
 989	int32_t y = background->sy - background->dy; \
 990	int32_t localX; \
 991	int32_t localY; \
 992	\
 993	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
 994	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
 995	flags |= FLAG_TARGET_2 * background->target2; \
 996	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
 997	color_t* palette = renderer->normalPalette; \
 998	if (variant) { \
 999		palette = renderer->variantPalette; \
1000	} \
1001	PREPARE_OBJWIN;
1002
1003#define BACKGROUND_BITMAP_ITERATE(W, H) \
1004	x += background->dx; \
1005	y += background->dy; \
1006	\
1007	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1008		continue; \
1009	} else { \
1010		localX = x; \
1011		localY = y; \
1012	}
1013
1014static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1015	int sizeAdjusted = 0x8000 << background->size;
1016
1017	BACKGROUND_BITMAP_INIT;
1018
1019	uint32_t screenBase = background->screenBase;
1020	uint32_t charBase = background->charBase;
1021	uint8_t mapData;
1022	uint8_t tileData;
1023
1024	int outX;
1025	for (outX = renderer->start; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1026		x += background->dx;
1027		y += background->dy;
1028
1029		if (background->overflow) {
1030			localX = x & (sizeAdjusted - 1);
1031			localY = y & (sizeAdjusted - 1);
1032		} else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
1033			continue;
1034		} else {
1035			localX = x;
1036			localY = y;
1037		}
1038		mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1039		tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1040
1041		uint32_t current = renderer->row[outX];
1042		if (tileData && !(current & FLAG_FINALIZED)) {
1043			if (!objwinSlowPath) {
1044				_composite(renderer, outX, palette[tileData] | flags, current);
1045			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1046				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1047				_composite(renderer, outX, currentPalette[tileData] | flags, current);
1048			}
1049		}
1050	}
1051}
1052
1053static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1054	BACKGROUND_BITMAP_INIT;
1055
1056	uint16_t color;
1057	uint32_t color32;
1058
1059	int outX;
1060	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1061		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1062
1063		color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1064		color32 = 0;
1065		color32 |= (color << 3) & 0xF8;
1066		color32 |= (color << 6) & 0xF800;
1067		color32 |= (color << 9) & 0xF80000;
1068
1069		uint32_t current = renderer->row[outX];
1070		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1071			if (!variant) {
1072				_composite(renderer, outX, color32 | flags, current);
1073			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1074				_composite(renderer, outX, _brighten(color32, renderer->bldy) | flags, current);
1075			} else if (renderer->blendEffect == BLEND_DARKEN) {
1076				_composite(renderer, outX, _darken(color32, renderer->bldy) | flags, current);
1077			}
1078		}
1079	}
1080}
1081
1082static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1083	BACKGROUND_BITMAP_INIT;
1084
1085	uint16_t color;
1086	uint32_t offset = 0;
1087	if (renderer->dispcnt.frameSelect) {
1088		offset = 0xA000;
1089	}
1090
1091	int outX;
1092	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1093		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1094
1095		color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1096
1097		uint32_t current = renderer->row[outX];
1098		if (color && !(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1099			if (!variant) {
1100				_composite(renderer, outX, renderer->normalPalette[color] | flags, current);
1101			} else {
1102				_composite(renderer, outX, renderer->variantPalette[color] | flags, current);
1103			}
1104		}
1105	}
1106}
1107
1108static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1109	BACKGROUND_BITMAP_INIT;
1110
1111	uint32_t color;
1112	uint32_t offset = 0;
1113	if (renderer->dispcnt.frameSelect) {
1114		offset = 0xA000;
1115	}
1116
1117	int outX;
1118	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1119		BACKGROUND_BITMAP_ITERATE(160, 128);
1120
1121		color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1122#ifndef COLOR_16_BIT
1123		color_t color32 = 0;
1124		color32 |= (color << 9) & 0xF80000;
1125		color32 |= (color << 3) & 0xF8;
1126		color32 |= (color << 6) & 0xF800;
1127		color = color32;
1128#endif
1129
1130		uint32_t current = renderer->row[outX];
1131		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1132			if (!variant) {
1133				_composite(renderer, outX, color | flags, current);
1134			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1135				_composite(renderer, outX, _brighten(color, renderer->bldy) | flags, current);
1136			} else if (renderer->blendEffect == BLEND_DARKEN) {
1137				_composite(renderer, outX, _darken(color, renderer->bldy) | flags, current);
1138			}
1139		}
1140	}
1141}
1142
1143static const int _objSizes[32] = {
1144	8, 8,
1145	16, 16,
1146	32, 32,
1147	64, 64,
1148	16, 8,
1149	32, 8,
1150	32, 16,
1151	64, 32,
1152	8, 16,
1153	8, 32,
1154	16, 32,
1155	32, 64,
1156	0, 0,
1157	0, 0,
1158	0, 0,
1159	0, 0
1160};
1161
1162#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1163		SPRITE_YBASE_ ## DEPTH(inY); \
1164		int outX = x >= start ? x : start; \
1165		int condition = x + width; \
1166		if (end < condition) { \
1167			condition = end; \
1168		} \
1169		for (; outX < condition; ++outX) { \
1170			int inX = outX - x; \
1171			if (sprite->hflip) { \
1172				inX = width - inX - 1; \
1173			} \
1174			if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1175				continue; \
1176			} \
1177			SPRITE_XBASE_ ## DEPTH(inX); \
1178			SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1179		}
1180
1181#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1182	int outX; \
1183	for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1184		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1185			continue; \
1186		} \
1187		int inX = outX - x; \
1188		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1189		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1190		\
1191		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1192			continue; \
1193		} \
1194		\
1195		SPRITE_YBASE_ ## DEPTH(localY); \
1196		SPRITE_XBASE_ ## DEPTH(localX); \
1197		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1198	}
1199
1200#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1201#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1202
1203#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1204	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1205	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1206	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1207		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1208	}
1209
1210#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1211	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1212	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1213	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1214		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1215	}
1216
1217#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1218	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1219	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1220	if (tileData) { \
1221		renderer->row[outX] |= FLAG_OBJWIN; \
1222	}
1223
1224#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1225#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1226
1227#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1228	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1229	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1230	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1231		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1232	}
1233
1234#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1235	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1236	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1237	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1238		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1239	}
1240
1241#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1242	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1243	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1244	if (tileData) { \
1245		renderer->row[outX] |= FLAG_OBJWIN; \
1246	}
1247
1248static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1249	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1250	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1251	int start = renderer->start;
1252	int end = renderer->end;
1253	if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1254		return;
1255	}
1256	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1257	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1258	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1259	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1260	int x = sprite->x;
1261	int inY = y - sprite->y;
1262	if (sprite->y + height - 256 >= 0) {
1263		inY += 256;
1264	}
1265	if (sprite->vflip) {
1266		inY = height - inY - 1;
1267	}
1268	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1269	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1270	if (!sprite->multipalette) {
1271		if (flags & FLAG_OBJWIN) {
1272			SPRITE_NORMAL_LOOP(16, OBJWIN);
1273		} else if (!variant) {
1274			SPRITE_NORMAL_LOOP(16, NORMAL);
1275		} else {
1276			SPRITE_NORMAL_LOOP(16, VARIANT);
1277		}
1278	} else {
1279		if (flags & FLAG_OBJWIN) {
1280			SPRITE_NORMAL_LOOP(256, OBJWIN);
1281		} else if (!variant) {
1282			SPRITE_NORMAL_LOOP(256, NORMAL);
1283		} else {
1284			SPRITE_NORMAL_LOOP(256, VARIANT);
1285		}
1286	}
1287}
1288
1289static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1290	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1291	int totalWidth = width << sprite->doublesize;
1292	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1293	int totalHeight = height << sprite->doublesize;
1294	int start = renderer->start;
1295	int end = renderer->end;
1296	if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1297		return;
1298	}
1299	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1300	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1301	flags |= FLAG_TARGET_2 * renderer->target2Obj;
1302	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1303	int x = sprite->x;
1304	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1305	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1306	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1307	int inY = y - sprite->y;
1308	if (inY < 0) {
1309		inY += 256;
1310	}
1311	if (!sprite->multipalette) {
1312		if (flags & FLAG_OBJWIN) {
1313			SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1314		} else if (!variant) {
1315			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1316		} else {
1317			SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1318		}
1319	} else {
1320		if (flags & FLAG_OBJWIN) {
1321			SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1322		} else if (!variant) {
1323			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1324		} else {
1325			SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1326		}
1327	}
1328}
1329
1330static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1331	int x;
1332	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
1333		uint32_t color = renderer->spriteLayer[x];
1334		uint32_t current = renderer->row[x];
1335		if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1336			_composite(renderer, x, color & ~FLAG_FINALIZED, current);
1337		}
1338	}
1339}
1340
1341static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1342	int i;
1343	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1344		for (i = 0; i < 512; ++i) {
1345			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1346		}
1347	} else if (renderer->blendEffect == BLEND_DARKEN) {
1348		for (i = 0; i < 512; ++i) {
1349			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1350		}
1351	} else {
1352		for (i = 0; i < 512; ++i) {
1353			renderer->variantPalette[i] = renderer->normalPalette[i];
1354		}
1355	}
1356}
1357
1358static inline color_t _brighten(color_t color, int y) {
1359	color_t c = 0;
1360	color_t a;
1361#ifdef COLOR_16_BIT
1362	a = color & 0x1F;
1363	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1364
1365	a = color & 0x3E0;
1366	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1367
1368	a = color & 0x7C00;
1369	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1370#else
1371	a = color & 0xF8;
1372	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1373
1374	a = color & 0xF800;
1375	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1376
1377	a = color & 0xF80000;
1378	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1379#endif
1380	return c;
1381}
1382
1383static inline color_t _darken(color_t color, int y) {
1384	color_t c = 0;
1385	color_t a;
1386#ifdef COLOR_16_BIT
1387	a = color & 0x1F;
1388	c |= (a - (a * y) / 16) & 0x1F;
1389
1390	a = color & 0x3E0;
1391	c |= (a - (a * y) / 16) & 0x3E0;
1392
1393	a = color & 0x7C00;
1394	c |= (a - (a * y) / 16) & 0x7C00;
1395#else
1396	a = color & 0xF8;
1397	c |= (a - (a * y) / 16) & 0xF8;
1398
1399	a = color & 0xF800;
1400	c |= (a - (a * y) / 16) & 0xF800;
1401
1402	a = color & 0xF80000;
1403	c |= (a - (a * y) / 16) & 0xF80000;
1404#endif
1405	return c;
1406}
1407
1408static color_t _mix(int weightA, color_t colorA, int weightB, color_t colorB) {
1409	color_t c = 0;
1410	color_t a, b;
1411#ifdef COLOR_16_BIT
1412	a = colorA & 0x1F;
1413	b = colorB & 0x1F;
1414	c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1415	if (c & 0x0020) {
1416		c = 0x001F;
1417	}
1418
1419	a = colorA & 0x3E0;
1420	b = colorB & 0x3E0;
1421	c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1422	if (c & 0x0400) {
1423		c |= 0x03E0;
1424	}
1425
1426	a = colorA & 0x7C00;
1427	b = colorB & 0x7C00;
1428	c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1429	if (c & 0x8000) {
1430		c |= 0x7C00;
1431	}
1432#else
1433	a = colorA & 0xF8;
1434	b = colorB & 0xF8;
1435	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1436	if (c & 0x00000100) {
1437		c = 0x000000F8;
1438	}
1439
1440	a = colorA & 0xF800;
1441	b = colorB & 0xF800;
1442	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1443	if (c & 0x00010000) {
1444		c = (c & 0x000000F8) | 0x0000F800;
1445	}
1446
1447	a = colorA & 0xF80000;
1448	b = colorB & 0xF80000;
1449	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1450	if (c & 0x01000000) {
1451		c = (c & 0x0000F8F8) | 0x00F80000;
1452	}
1453#endif
1454	return c;
1455}