all repos — mgba @ 5a685558c72bd113dd6d80145b9b623fe304155b

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