all repos — mgba @ 17ccb35c33c80580d0e2043413bd045e4d201ab1

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