all repos — mgba @ 53a52d8cf8954a1689cc0b8da79dab0ca09ee1f7

mGBA Game Boy Advance Emulator

src/gba/renderers/video-software.c (view raw)

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