all repos — mgba @ 228b6aaa016e7e249ac54504f9c42b2732261cd3

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			}
 601			if (TEST_LAYER_ENABLED(3)) {
 602				switch (renderer->dispcnt.mode) {
 603				case 0:
 604					_drawBackgroundMode0(renderer, &renderer->bg[3], y);
 605					break;
 606				case 2:
 607					_drawBackgroundMode2(renderer, &renderer->bg[3], y);
 608					break;
 609				}
 610			}
 611		}
 612	}
 613	renderer->bg[2].sx += renderer->bg[2].dmx;
 614	renderer->bg[2].sy += renderer->bg[2].dmy;
 615	renderer->bg[3].sx += renderer->bg[3].dmx;
 616	renderer->bg[3].sy += renderer->bg[3].dmy;
 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 & FLAG_ORDER_MASK) < (current & FLAG_ORDER_MASK)) {
 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 PREPARE_OBJWIN \
 681	int objwinSlowPath = renderer->dispcnt.objwinEnable; \
 682	int objwinOnly = 0; \
 683	int objwinForceEnable = 0; \
 684	color_t* objwinPalette; \
 685	if (objwinSlowPath) { \
 686		if (background->target1 && renderer->objwin.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
 687			objwinPalette = renderer->variantPalette; \
 688		} else { \
 689			objwinPalette = renderer->normalPalette; \
 690		} \
 691		switch (background->index) { \
 692		case 0: \
 693			objwinForceEnable = renderer->objwin.bg0Enable && renderer->currentWindow.bg0Enable; \
 694			objwinOnly = !renderer->objwin.bg0Enable; \
 695			break; \
 696		case 1: \
 697			objwinForceEnable = renderer->objwin.bg1Enable && renderer->currentWindow.bg1Enable; \
 698			objwinOnly = !renderer->objwin.bg1Enable; \
 699			break; \
 700		case 2: \
 701			objwinForceEnable = renderer->objwin.bg2Enable && renderer->currentWindow.bg2Enable; \
 702			objwinOnly = !renderer->objwin.bg2Enable; \
 703			break; \
 704		case 3: \
 705			objwinForceEnable = renderer->objwin.bg3Enable && renderer->currentWindow.bg3Enable; \
 706			objwinOnly = !renderer->objwin.bg3Enable; \
 707			break; \
 708		} \
 709	}
 710
 711static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
 712	int inX = renderer->start + background->x;
 713	int inY = y + background->y;
 714	union GBATextMapData mapData;
 715
 716	unsigned yBase = inY & 0xF8;
 717	if (background->size == 2) {
 718		yBase += inY & 0x100;
 719	} else if (background->size == 3) {
 720		yBase += (inY & 0x100) << 1;
 721	}
 722
 723	int localX;
 724	int localY;
 725
 726	unsigned xBase;
 727
 728	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
 729	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
 730	flags |= FLAG_TARGET_2 * background->target2;
 731
 732	uint32_t screenBase;
 733	uint32_t charBase;
 734	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
 735	color_t* palette = renderer->normalPalette;
 736	if (variant) {
 737		palette = renderer->variantPalette;
 738	}
 739	PREPARE_OBJWIN;
 740
 741	int outX = renderer->start;
 742	int tileX = 0;
 743	int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
 744	if (inX & 0x7) {
 745		uint32_t tileData;
 746		uint32_t current;
 747		int pixelData, paletteData;
 748		int mod8 = inX & 0x7;
 749		BACKGROUND_TEXT_SELECT_CHARACTER;
 750
 751		int end = outX + 0x8 - mod8;
 752		if (!background->multipalette) {
 753			paletteData = mapData.palette << 4;
 754			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 755			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 756			if (!mapData.hflip) {
 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 + (mapData.tile << 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		uint32_t tileData;
 790		uint32_t current;
 791		int pixelData, paletteData;
 792		int mod8 = (inX + renderer->end - renderer->start) & 0x7;
 793		BACKGROUND_TEXT_SELECT_CHARACTER;
 794
 795		int end = 0x8 - mod8;
 796		if (!background->multipalette) {
 797			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 798			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 799			paletteData = mapData.palette << 4;
 800			if (!mapData.hflip) {
 801				outX = renderer->end - mod8;
 802				if (outX < 0) {
 803					tileData >>= 4 * -outX;
 804					outX = 0;
 805				}
 806				for (; outX < renderer->end; ++outX) {
 807					BACKGROUND_DRAW_PIXEL_16;
 808				}
 809			} else {
 810				tileData >>= 4 * (0x8 - mod8);
 811				int end2 = renderer->end - 8;
 812				if (end2 < -1) {
 813					end2 = -1;
 814				}
 815				for (outX = renderer->end - 1; outX > end2; --outX) {
 816					BACKGROUND_DRAW_PIXEL_16;
 817				}
 818			}
 819		} else {
 820			// TODO: hflip
 821			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 822			outX = renderer->end - 8 + end;
 823			int end2 = 4 - end;
 824			if (end2 > 0) {
 825				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 826				for (; outX < renderer->end - end2; ++outX) {
 827					BACKGROUND_DRAW_PIXEL_256;
 828				}
 829				++charBase;
 830			}
 831
 832			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 833			for (; outX < renderer->end; ++outX) {
 834				BACKGROUND_DRAW_PIXEL_256;
 835			}
 836		}
 837
 838		tileX = (inX & 0x7) != 0;
 839		outX = renderer->start + tileX * 8 - (inX & 0x7);
 840	}
 841
 842	if (!background->multipalette) {
 843		uint32_t tileData;
 844		uint32_t current;
 845		int paletteData, pixelData;
 846		for (; tileX < tileEnd; ++tileX) {
 847			BACKGROUND_TEXT_SELECT_CHARACTER;
 848			paletteData = mapData.palette << 4;
 849			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 850			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 851			if (tileData) {
 852				if (!mapData.hflip) {
 853					BACKGROUND_DRAW_PIXEL_16;
 854					++outX;
 855					BACKGROUND_DRAW_PIXEL_16;
 856					++outX;
 857					BACKGROUND_DRAW_PIXEL_16;
 858					++outX;
 859					BACKGROUND_DRAW_PIXEL_16;
 860					++outX;
 861					BACKGROUND_DRAW_PIXEL_16;
 862					++outX;
 863					BACKGROUND_DRAW_PIXEL_16;
 864					++outX;
 865					BACKGROUND_DRAW_PIXEL_16;
 866					++outX;
 867					BACKGROUND_DRAW_PIXEL_16;
 868					++outX;
 869				} else {
 870					outX += 7;
 871					BACKGROUND_DRAW_PIXEL_16;
 872					--outX;
 873					BACKGROUND_DRAW_PIXEL_16;
 874					--outX;
 875					BACKGROUND_DRAW_PIXEL_16;
 876					--outX;
 877					BACKGROUND_DRAW_PIXEL_16;
 878					--outX;
 879					BACKGROUND_DRAW_PIXEL_16;
 880					--outX;
 881					BACKGROUND_DRAW_PIXEL_16;
 882					--outX;
 883					BACKGROUND_DRAW_PIXEL_16;
 884					--outX;
 885					BACKGROUND_DRAW_PIXEL_16;
 886					outX += 8;
 887				}
 888			} else {
 889				outX += 8;
 890			}
 891		}
 892	} else {
 893		uint32_t tileData;
 894		uint32_t current;
 895		int pixelData;
 896		for (; tileX < tileEnd; ++tileX) {
 897			BACKGROUND_TEXT_SELECT_CHARACTER;
 898			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 899			if (!mapData.hflip) {
 900				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 901				if (tileData) {
 902						BACKGROUND_DRAW_PIXEL_256;
 903						++outX;
 904						BACKGROUND_DRAW_PIXEL_256;
 905						++outX;
 906						BACKGROUND_DRAW_PIXEL_256;
 907						++outX;
 908						BACKGROUND_DRAW_PIXEL_256;
 909						++outX;
 910				} else {
 911					outX += 4;
 912				}
 913				tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 914				if (tileData) {
 915						BACKGROUND_DRAW_PIXEL_256;
 916						++outX;
 917						BACKGROUND_DRAW_PIXEL_256;
 918						++outX;
 919						BACKGROUND_DRAW_PIXEL_256;
 920						++outX;
 921						BACKGROUND_DRAW_PIXEL_256;
 922						++outX;
 923				} else {
 924					outX += 4;
 925				}
 926			} else {
 927				uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 928				if (tileData) {
 929					outX += 3;
 930					BACKGROUND_DRAW_PIXEL_256;
 931					--outX;
 932					BACKGROUND_DRAW_PIXEL_256;
 933					--outX;
 934					BACKGROUND_DRAW_PIXEL_256;
 935					--outX;
 936					BACKGROUND_DRAW_PIXEL_256;
 937					outX += 4;
 938				} else {
 939					outX += 4;
 940				}
 941				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 942				if (tileData) {
 943					outX += 3;
 944					BACKGROUND_DRAW_PIXEL_256;
 945					--outX;
 946					BACKGROUND_DRAW_PIXEL_256;
 947					--outX;
 948					BACKGROUND_DRAW_PIXEL_256;
 949					--outX;
 950					BACKGROUND_DRAW_PIXEL_256;
 951					outX += 4;
 952				} else {
 953					outX += 4;
 954				}
 955			}
 956		}
 957	}
 958}
 959
 960#define BACKGROUND_BITMAP_INIT \
 961	(void)(unused); \
 962	int32_t x = background->sx - background->dx; \
 963	int32_t y = background->sy - background->dy; \
 964	int32_t localX; \
 965	int32_t localY; \
 966	\
 967	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
 968	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
 969	flags |= FLAG_TARGET_2 * background->target2; \
 970	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
 971	color_t* palette = renderer->normalPalette; \
 972	if (variant) { \
 973		palette = renderer->variantPalette; \
 974	} \
 975	PREPARE_OBJWIN;
 976
 977#define BACKGROUND_BITMAP_ITERATE(W, H) \
 978	x += background->dx; \
 979	y += background->dy; \
 980	\
 981	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
 982		continue; \
 983	} else { \
 984		localX = x; \
 985		localY = y; \
 986	}
 987
 988static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
 989	int sizeAdjusted = 0x8000 << background->size;
 990
 991	BACKGROUND_BITMAP_INIT;
 992
 993	uint32_t screenBase = background->screenBase;
 994	uint32_t charBase = background->charBase;
 995	uint8_t mapData;
 996	uint8_t tileData;
 997
 998	int outX;
 999	for (outX = renderer->start; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1000		x += background->dx;
1001		y += background->dy;
1002
1003		if (background->overflow) {
1004			localX = x & (sizeAdjusted - 1);
1005			localY = y & (sizeAdjusted - 1);
1006		} else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
1007			continue;
1008		} else {
1009			localX = x;
1010			localY = y;
1011		}
1012		mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1013		tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1014
1015		uint32_t current = renderer->row[outX];
1016		if (tileData && !(current & FLAG_FINALIZED)) {
1017			if (!objwinSlowPath) {
1018				_composite(renderer, outX, palette[tileData] | flags, current);
1019			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1020				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1021				_composite(renderer, outX, currentPalette[tileData] | flags, current);
1022			}
1023		}
1024	}
1025}
1026
1027static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1028	BACKGROUND_BITMAP_INIT;
1029
1030	uint16_t color;
1031	uint32_t color32;
1032
1033	int outX;
1034	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1035		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1036
1037		color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1038		color32 = 0;
1039		color32 |= (color << 3) & 0xF8;
1040		color32 |= (color << 6) & 0xF800;
1041		color32 |= (color << 9) & 0xF80000;
1042
1043		uint32_t current = renderer->row[outX];
1044		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1045			if (!variant) {
1046				_composite(renderer, outX, color32 | flags, current);
1047			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1048				_composite(renderer, outX, _brighten(color32, renderer->bldy) | flags, current);
1049			} else if (renderer->blendEffect == BLEND_DARKEN) {
1050				_composite(renderer, outX, _darken(color32, renderer->bldy) | flags, current);
1051			}
1052		}
1053	}
1054}
1055
1056static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1057	BACKGROUND_BITMAP_INIT;
1058
1059	uint16_t color;
1060	uint32_t offset = 0;
1061	if (renderer->dispcnt.frameSelect) {
1062		offset = 0xA000;
1063	}
1064
1065	int outX;
1066	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1067		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1068
1069		color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1070
1071		uint32_t current = renderer->row[outX];
1072		if (color && !(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1073			if (!variant) {
1074				_composite(renderer, outX, renderer->normalPalette[color] | flags, current);
1075			} else {
1076				_composite(renderer, outX, renderer->variantPalette[color] | flags, current);
1077			}
1078		}
1079	}
1080}
1081
1082static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1083	BACKGROUND_BITMAP_INIT;
1084
1085	uint32_t color;
1086	uint32_t offset = 0;
1087	if (renderer->dispcnt.frameSelect) {
1088		offset = 0xA000;
1089	}
1090
1091	int outX;
1092	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1093		BACKGROUND_BITMAP_ITERATE(160, 128);
1094
1095		color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1096#ifndef COLOR_16_BIT
1097		color_t color32 = 0;
1098		color32 |= (color << 9) & 0xF80000;
1099		color32 |= (color << 3) & 0xF8;
1100		color32 |= (color << 6) & 0xF800;
1101		color = color32;
1102#endif
1103
1104		uint32_t current = renderer->row[outX];
1105		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1106			if (!variant) {
1107				_composite(renderer, outX, color | flags, current);
1108			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1109				_composite(renderer, outX, _brighten(color, renderer->bldy) | flags, current);
1110			} else if (renderer->blendEffect == BLEND_DARKEN) {
1111				_composite(renderer, outX, _darken(color, renderer->bldy) | flags, current);
1112			}
1113		}
1114	}
1115}
1116
1117static const int _objSizes[32] = {
1118	8, 8,
1119	16, 16,
1120	32, 32,
1121	64, 64,
1122	16, 8,
1123	32, 8,
1124	32, 16,
1125	64, 32,
1126	8, 16,
1127	8, 32,
1128	16, 32,
1129	32, 64,
1130	0, 0,
1131	0, 0,
1132	0, 0,
1133	0, 0
1134};
1135
1136#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1137		SPRITE_YBASE_ ## DEPTH(inY); \
1138		int outX = x >= start ? x : start; \
1139		int condition = x + width; \
1140		if (end < condition) { \
1141			condition = end; \
1142		} \
1143		for (; outX < condition; ++outX) { \
1144			int inX = outX - x; \
1145			if (sprite->hflip) { \
1146				inX = width - inX - 1; \
1147			} \
1148			if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1149				continue; \
1150			} \
1151			SPRITE_XBASE_ ## DEPTH(inX); \
1152			SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1153		}
1154
1155#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1156	int outX; \
1157	for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1158		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1159			continue; \
1160		} \
1161		int inX = outX - x; \
1162		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1163		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1164		\
1165		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1166			continue; \
1167		} \
1168		\
1169		SPRITE_YBASE_ ## DEPTH(localY); \
1170		SPRITE_XBASE_ ## DEPTH(localX); \
1171		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1172	}
1173
1174#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1175#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1176
1177#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1178	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1179	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1180	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1181		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1182	}
1183
1184#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1185	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1186	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1187	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1188		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1189	}
1190
1191#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1192	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1193	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1194	if (tileData) { \
1195		renderer->row[outX] |= FLAG_OBJWIN; \
1196	}
1197
1198#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1199#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1200
1201#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1202	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1203	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1204	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1205		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1206	}
1207
1208#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1209	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1210	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1211	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1212		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1213	}
1214
1215#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1216	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1217	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1218	if (tileData) { \
1219		renderer->row[outX] |= FLAG_OBJWIN; \
1220	}
1221
1222static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1223	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1224	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1225	int start = renderer->start;
1226	int end = renderer->end;
1227	if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1228		return;
1229	}
1230	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1231	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1232	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1233	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1234	int x = sprite->x;
1235	int inY = y - sprite->y;
1236	if (sprite->y + height - 256 >= 0) {
1237		inY += 256;
1238	}
1239	if (sprite->vflip) {
1240		inY = height - inY - 1;
1241	}
1242	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1243	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1244	if (!sprite->multipalette) {
1245		if (flags & FLAG_OBJWIN) {
1246			SPRITE_NORMAL_LOOP(16, OBJWIN);
1247		} else if (!variant) {
1248			SPRITE_NORMAL_LOOP(16, NORMAL);
1249		} else {
1250			SPRITE_NORMAL_LOOP(16, VARIANT);
1251		}
1252	} else {
1253		if (flags & FLAG_OBJWIN) {
1254			SPRITE_NORMAL_LOOP(256, OBJWIN);
1255		} else if (!variant) {
1256			SPRITE_NORMAL_LOOP(256, NORMAL);
1257		} else {
1258			SPRITE_NORMAL_LOOP(256, VARIANT);
1259		}
1260	}
1261}
1262
1263static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1264	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1265	int totalWidth = width << sprite->doublesize;
1266	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1267	int totalHeight = height << sprite->doublesize;
1268	int start = renderer->start;
1269	int end = renderer->end;
1270	if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1271		return;
1272	}
1273	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1274	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1275	flags |= FLAG_TARGET_2 * renderer->target2Obj;
1276	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1277	int x = sprite->x;
1278	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1279	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1280	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && sprite->mode != OBJ_MODE_SEMITRANSPARENT && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1281	int inY = y - sprite->y;
1282	if (inY < 0) {
1283		inY += 256;
1284	}
1285	if (!sprite->multipalette) {
1286		if (flags & FLAG_OBJWIN) {
1287			SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1288		} else if (!variant) {
1289			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1290		} else {
1291			SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1292		}
1293	} else {
1294		if (flags & FLAG_OBJWIN) {
1295			SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1296		} else if (!variant) {
1297			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1298		} else {
1299			SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1300		}
1301	}
1302}
1303
1304static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1305	int x;
1306	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
1307		uint32_t color = renderer->spriteLayer[x];
1308		uint32_t current = renderer->row[x];
1309		if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1310			_composite(renderer, x, color & ~FLAG_FINALIZED, current);
1311		}
1312	}
1313}
1314
1315static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1316	int i;
1317	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1318		for (i = 0; i < 512; ++i) {
1319			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1320		}
1321	} else if (renderer->blendEffect == BLEND_DARKEN) {
1322		for (i = 0; i < 512; ++i) {
1323			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1324		}
1325	} else {
1326		for (i = 0; i < 512; ++i) {
1327			renderer->variantPalette[i] = renderer->normalPalette[i];
1328		}
1329	}
1330}
1331
1332static inline color_t _brighten(color_t color, int y) {
1333	color_t c = 0;
1334	color_t a;
1335#ifdef COLOR_16_BIT
1336	a = color & 0x1F;
1337	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1338
1339	a = color & 0x3E0;
1340	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1341
1342	a = color & 0x7C00;
1343	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1344#else
1345	a = color & 0xF8;
1346	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1347
1348	a = color & 0xF800;
1349	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1350
1351	a = color & 0xF80000;
1352	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1353#endif
1354	return c;
1355}
1356
1357static inline color_t _darken(color_t color, int y) {
1358	color_t c = 0;
1359	color_t a;
1360#ifdef COLOR_16_BIT
1361	a = color & 0x1F;
1362	c |= (a - (a * y) / 16) & 0x1F;
1363
1364	a = color & 0x3E0;
1365	c |= (a - (a * y) / 16) & 0x3E0;
1366
1367	a = color & 0x7C00;
1368	c |= (a - (a * y) / 16) & 0x7C00;
1369#else
1370	a = color & 0xF8;
1371	c |= (a - (a * y) / 16) & 0xF8;
1372
1373	a = color & 0xF800;
1374	c |= (a - (a * y) / 16) & 0xF800;
1375
1376	a = color & 0xF80000;
1377	c |= (a - (a * y) / 16) & 0xF80000;
1378#endif
1379	return c;
1380}
1381
1382static color_t _mix(int weightA, color_t colorA, int weightB, color_t colorB) {
1383	color_t c = 0;
1384	color_t a, b;
1385#ifdef COLOR_16_BIT
1386	a = colorA & 0x1F;
1387	b = colorB & 0x1F;
1388	c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1389	if (c & 0x0020) {
1390		c = 0x001F;
1391	}
1392
1393	a = colorA & 0x3E0;
1394	b = colorB & 0x3E0;
1395	c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1396	if (c & 0x0400) {
1397		c |= 0x03E0;
1398	}
1399
1400	a = colorA & 0x7C00;
1401	b = colorB & 0x7C00;
1402	c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1403	if (c & 0x8000) {
1404		c |= 0x7C00;
1405	}
1406#else
1407	a = colorA & 0xF8;
1408	b = colorB & 0xF8;
1409	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1410	if (c & 0x00000100) {
1411		c = 0x000000F8;
1412	}
1413
1414	a = colorA & 0xF800;
1415	b = colorB & 0xF800;
1416	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1417	if (c & 0x00010000) {
1418		c = (c & 0x000000F8) | 0x0000F800;
1419	}
1420
1421	a = colorA & 0xF80000;
1422	b = colorB & 0xF80000;
1423	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1424	if (c & 0x01000000) {
1425		c = (c & 0x0000F8F8) | 0x00F80000;
1426	}
1427#endif
1428	return c;
1429}