all repos — mgba @ d5291eb1b65c29b629c8d10d65d6cdf2d4449e48

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