all repos — mgba @ cf298474bcaf57a6c10cfcbe050eb61e630c269f

mGBA Game Boy Advance Emulator

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

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