all repos — mgba @ e789b324dcc2a502dc92d521c139e34584d3a996

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