all repos — mgba @ 27f5468e01d4361705a1dd0615c195e138e17710

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->winin.packed = value;
 263		break;
 264	case REG_WINOUT:
 265		softwareRenderer->winout.packed = value;
 266		break;
 267	default:
 268		GBALog(GBA_LOG_STUB, "Stub video register write: %03x", address);
 269	}
 270	return value;
 271}
 272
 273static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
 274	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 275	if ((oam & 0x3) != 0x3) {
 276		oam >>= 2;
 277		struct GBAObj* sprite = &renderer->oam->obj[oam];
 278		int enabled = sprite->transformed || !sprite->disable;
 279		enabled <<= (oam & 0x1F);
 280		softwareRenderer->enabledBitmap[oam >> 5] = (softwareRenderer->enabledBitmap[oam >> 5] & ~(1 << (oam & 0x1F))) | enabled;
 281	}
 282}
 283
 284static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 285	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 286	uint32_t color32 = 0;
 287	color32 |= (value << 3) & 0xF8;
 288	color32 |= (value << 6) & 0xF800;
 289	color32 |= (value << 9) & 0xF80000;
 290	softwareRenderer->normalPalette[address >> 1] = color32;
 291	if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
 292		softwareRenderer->variantPalette[address >> 1] = _brighten(color32, softwareRenderer->bldy);
 293	} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
 294		softwareRenderer->variantPalette[address >> 1] = _darken(color32, softwareRenderer->bldy);
 295	}
 296}
 297
 298static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
 299	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 300	if (renderer->frameskip > 0) {
 301		return;
 302	}
 303	uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
 304	if (softwareRenderer->dispcnt.forcedBlank) {
 305		for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 306			row[x] = GBA_COLOR_WHITE;
 307		}
 308		return;
 309	} else {
 310		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
 311		if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA) {
 312			backdrop |= softwareRenderer->normalPalette[0];
 313		} else {
 314			backdrop |= softwareRenderer->variantPalette[0];
 315		}
 316		for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 317			row[x] = backdrop;
 318		}
 319	}
 320
 321	softwareRenderer->row = row;
 322
 323	softwareRenderer->start = 0;
 324	softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
 325	_drawScanline(softwareRenderer, y);
 326}
 327
 328static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
 329	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 330
 331	pthread_mutex_lock(&softwareRenderer->mutex);
 332	if (renderer->frameskip > 0) {
 333		--renderer->frameskip;
 334	} else {
 335		renderer->framesPending++;
 336		pthread_cond_broadcast(&softwareRenderer->upCond);
 337		if (!renderer->turbo) {
 338			pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
 339		}
 340	}
 341	pthread_mutex_unlock(&softwareRenderer->mutex);
 342
 343	softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
 344	softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
 345	softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
 346	softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
 347}
 348
 349static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
 350	renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
 351	renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
 352	renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
 353	renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
 354}
 355
 356static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 357	(void)(renderer);
 358	union GBARegisterBGCNT reg = { .packed = value };
 359	bg->priority = reg.priority;
 360	bg->charBase = reg.charBase << 14;
 361	bg->mosaic = reg.mosaic;
 362	bg->multipalette = reg.multipalette;
 363	bg->screenBase = reg.screenBase << 11;
 364	bg->overflow = reg.overflow;
 365	bg->size = reg.size;
 366}
 367
 368static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 369	bg->dx = value;
 370}
 371
 372static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 373	bg->dmx = value;
 374}
 375
 376static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 377	bg->dy = value;
 378}
 379
 380static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 381	bg->dmy = value;
 382}
 383
 384static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 385	bg->refx = (bg->refx & 0xFFFF0000) | value;
 386	bg->sx = bg->refx;
 387}
 388
 389static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 390	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
 391	bg->refx <<= 4;
 392	bg->refx >>= 4;
 393	bg->sx = bg->refx;
 394}
 395
 396static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 397	bg->refy = (bg->refy & 0xFFFF0000) | value;
 398	bg->sy = bg->refy;
 399}
 400
 401static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 402	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
 403	bg->refy <<= 4;
 404	bg->refy >>= 4;
 405	bg->sy = bg->refy;
 406}
 407
 408static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
 409	union {
 410		struct {
 411			unsigned target1Bg0 : 1;
 412			unsigned target1Bg1 : 1;
 413			unsigned target1Bg2 : 1;
 414			unsigned target1Bg3 : 1;
 415			unsigned target1Obj : 1;
 416			unsigned target1Bd : 1;
 417			enum BlendEffect effect : 2;
 418			unsigned target2Bg0 : 1;
 419			unsigned target2Bg1 : 1;
 420			unsigned target2Bg2 : 1;
 421			unsigned target2Bg3 : 1;
 422			unsigned target2Obj : 1;
 423			unsigned target2Bd : 1;
 424		};
 425		uint16_t packed;
 426	} bldcnt = { .packed = value };
 427
 428	enum BlendEffect oldEffect = renderer->blendEffect;
 429
 430	renderer->bg[0].target1 = bldcnt.target1Bg0;
 431	renderer->bg[1].target1 = bldcnt.target1Bg1;
 432	renderer->bg[2].target1 = bldcnt.target1Bg2;
 433	renderer->bg[3].target1 = bldcnt.target1Bg3;
 434	renderer->bg[0].target2 = bldcnt.target2Bg0;
 435	renderer->bg[1].target2 = bldcnt.target2Bg1;
 436	renderer->bg[2].target2 = bldcnt.target2Bg2;
 437	renderer->bg[3].target2 = bldcnt.target2Bg3;
 438
 439	renderer->blendEffect = bldcnt.effect;
 440	renderer->target1Obj = bldcnt.target1Obj;
 441	renderer->target1Bd = bldcnt.target1Bd;
 442	renderer->target2Obj = bldcnt.target2Obj;
 443	renderer->target2Bd = bldcnt.target2Bd;
 444
 445	if (oldEffect != renderer->blendEffect) {
 446		_updatePalettes(renderer);
 447	}
 448}
 449
 450static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
 451	int i;
 452	memset(renderer->spriteLayer, 0, sizeof(renderer->spriteLayer));
 453	if (renderer->dispcnt.objEnable) {
 454		int j;
 455		for (j = 0; j < 4; ++j) {
 456			uint32_t bitmap = renderer->enabledBitmap[j];
 457			if (!bitmap) {
 458				continue;
 459			}
 460			for (i = j * 32; i < (j + 1) * 32; ++i) {
 461				if (bitmap & 1) {
 462					struct GBAObj* sprite = &renderer->d.oam->obj[i];
 463					if (sprite->transformed) {
 464						_preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
 465					} else {
 466						_preprocessSprite(renderer, sprite, y);
 467					}
 468				}
 469				bitmap >>= 1;
 470			}
 471		}
 472	}
 473
 474	int priority;
 475	for (priority = 0; priority < 4; ++priority) {
 476		_postprocessSprite(renderer, priority);
 477		if (renderer->bg[0].enabled && renderer->bg[0].priority == priority && renderer->dispcnt.mode < 2) {
 478			_drawBackgroundMode0(renderer, &renderer->bg[0], y);
 479		}
 480		if (renderer->bg[1].enabled && renderer->bg[1].priority == priority && renderer->dispcnt.mode < 2) {
 481			_drawBackgroundMode0(renderer, &renderer->bg[1], y);
 482		}
 483		if (renderer->bg[2].enabled && renderer->bg[2].priority == priority) {
 484			switch (renderer->dispcnt.mode) {
 485			case 0:
 486				_drawBackgroundMode0(renderer, &renderer->bg[2], y);
 487				break;
 488			case 1:
 489			case 2:
 490				_drawBackgroundMode2(renderer, &renderer->bg[2], y);
 491				break;
 492			case 3:
 493				_drawBackgroundMode3(renderer, &renderer->bg[2], y);
 494				break;
 495			case 4:
 496				_drawBackgroundMode4(renderer, &renderer->bg[2], y);
 497				break;
 498			case 5:
 499				_drawBackgroundMode5(renderer, &renderer->bg[2], y);
 500				break;
 501			}
 502			renderer->bg[2].sx += renderer->bg[2].dmx;
 503			renderer->bg[2].sy += renderer->bg[2].dmy;
 504		}
 505		if (renderer->bg[3].enabled && renderer->bg[3].priority == priority) {
 506			switch (renderer->dispcnt.mode) {
 507			case 0:
 508				_drawBackgroundMode0(renderer, &renderer->bg[3], y);
 509				break;
 510			case 2:
 511				_drawBackgroundMode2(renderer, &renderer->bg[3], y);
 512				break;
 513			}
 514			renderer->bg[3].sx += renderer->bg[3].dmx;
 515			renderer->bg[3].sy += renderer->bg[3].dmy;
 516		}
 517	}
 518}
 519
 520static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color) {
 521	uint32_t current = renderer->row[offset];
 522	// We stash the priority on the top bits so we cn do a one-operator comparison
 523	// The lower the number, the higher the priority, and sprites take precendence over backgrounds
 524	// We want to do special processing if the color pixel is target 1, however
 525	if (color < current) {
 526		if (current & FLAG_UNWRITTEN) {
 527			renderer->row[offset] = color;
 528		} else if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
 529			renderer->row[offset] = color | FLAG_FINALIZED;
 530		} else {
 531			renderer->row[offset] = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
 532		}
 533	} else {
 534		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 535			renderer->row[offset] = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
 536		} else {
 537			renderer->row[offset] = current | FLAG_FINALIZED;
 538		}
 539	}
 540}
 541
 542#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
 543	pixelData = tileData & 0xF; \
 544	if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
 545		_composite(renderer, outX, renderer->normalPalette[pixelData | paletteData] | flags); \
 546	} \
 547	tileData >>= 4;
 548
 549#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
 550	pixelData = tileData & 0xF; \
 551	if (tileData & 0xF && !(renderer->row[outX] & FLAG_FINALIZED)) { \
 552		_composite(renderer, outX, renderer->variantPalette[pixelData | paletteData] | flags); \
 553	} \
 554	tileData >>= 4;
 555
 556#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
 557	pixelData = tileData & 0xFF; \
 558	if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
 559		_composite(renderer, outX, renderer->normalPalette[pixelData] | flags); \
 560	} \
 561	tileData >>= 8;
 562
 563#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
 564	pixelData = tileData & 0xFF; \
 565	if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
 566		_composite(renderer, outX, renderer->variantPalette[pixelData] | flags); \
 567	} \
 568	tileData >>= 8;
 569
 570#define BACKGROUND_TEXT_SELECT_CHARACTER \
 571	localX = tileX * 8 + inX; \
 572	xBase = localX & 0xF8; \
 573	if (background->size & 1) { \
 574		xBase += (localX & 0x100) << 5; \
 575	} \
 576	screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
 577	mapData.packed = renderer->d.vram[screenBase]; \
 578	if (!mapData.vflip) { \
 579		localY = inY & 0x7; \
 580	} else { \
 581		localY = 7 - (inY & 0x7); \
 582	}
 583
 584#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
 585	uint32_t tileData; \
 586	int paletteData, pixelData; \
 587	for (; tileX < 30; ++tileX) { \
 588		BACKGROUND_TEXT_SELECT_CHARACTER; \
 589		paletteData = mapData.palette << 4; \
 590		charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
 591		tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 592		if (tileData) { \
 593			if (!mapData.hflip) { \
 594				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 595				++outX; \
 596				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 597				++outX; \
 598				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 599				++outX; \
 600				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 601				++outX; \
 602				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 603				++outX; \
 604				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 605				++outX; \
 606				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 607				++outX; \
 608				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 609				++outX; \
 610			} else { \
 611				outX += 7; \
 612				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 613				--outX; \
 614				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 615				--outX; \
 616				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 617				--outX; \
 618				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 619				--outX; \
 620				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 621				--outX; \
 622				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 623				--outX; \
 624				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 625				--outX; \
 626				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
 627				outX += 8; \
 628			} \
 629		} else { \
 630			outX += 8; \
 631		} \
 632	}
 633
 634#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
 635	uint32_t tileData; \
 636	int pixelData; \
 637	for (; tileX < 30; ++tileX) { \
 638		BACKGROUND_TEXT_SELECT_CHARACTER; \
 639		charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
 640		if (!mapData.hflip) { \
 641			tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 642			if (tileData) { \
 643					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 644					++outX; \
 645					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 646					++outX; \
 647					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 648					++outX; \
 649					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 650					++outX; \
 651			} else { \
 652				outX += 4; \
 653			} \
 654			tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
 655			if (tileData) { \
 656					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 657					++outX; \
 658					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 659					++outX; \
 660					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 661					++outX; \
 662					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 663					++outX; \
 664			} else { \
 665				outX += 4; \
 666			} \
 667		} else { \
 668			uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
 669			if (tileData) { \
 670				outX += 3; \
 671				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 672				--outX; \
 673				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 674				--outX; \
 675				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 676				--outX; \
 677				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 678				outX += 4; \
 679			} else { \
 680				outX += 4; \
 681			} \
 682			tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
 683			if (tileData) { \
 684				outX += 3; \
 685				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 686				--outX; \
 687				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 688				--outX; \
 689				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 690				--outX; \
 691				BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
 692				outX += 4; \
 693			} else { \
 694				outX += 4; \
 695			} \
 696		} \
 697	}
 698
 699static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
 700	int inX = background->x;
 701	int inY = y + background->y;
 702	union GBATextMapData mapData;
 703
 704	unsigned yBase = inY & 0xF8;
 705	if (background->size == 2) {
 706		yBase += inY & 0x100;
 707	} else if (background->size == 3) {
 708		yBase += (inY & 0x100) << 1;
 709	}
 710
 711	int localX;
 712	int localY;
 713
 714	unsigned xBase;
 715
 716	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
 717	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
 718	flags |= FLAG_TARGET_2 * background->target2;
 719
 720	uint32_t screenBase;
 721	uint32_t charBase;
 722	int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
 723
 724	int outX = 0;
 725	int tileX = 0;
 726	if (inX & 0x7) {
 727		uint32_t tileData;
 728		int pixelData, paletteData;
 729		BACKGROUND_TEXT_SELECT_CHARACTER;
 730
 731		int end = 0x8 - (inX & 0x7);
 732		if (!background->multipalette) {
 733			paletteData = mapData.palette << 4;
 734			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 735			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 736			if (!mapData.hflip) {
 737				tileData >>= 4 * (inX & 0x7);
 738				if (!variant) {
 739					for (outX = 0; outX < end; ++outX) {
 740						BACKGROUND_DRAW_PIXEL_16_NORMAL;
 741					}
 742				} else {
 743					for (outX = 0; outX < end; ++outX) {
 744						BACKGROUND_DRAW_PIXEL_16_VARIANT;
 745					}
 746				}
 747			} else {
 748				if (!variant) {
 749					for (outX = end; outX--;) {
 750						BACKGROUND_DRAW_PIXEL_16_NORMAL;
 751					}
 752				} else {
 753					for (outX = end; outX--;) {
 754						BACKGROUND_DRAW_PIXEL_16_VARIANT;
 755					}
 756				}
 757			}
 758		} else {
 759			// TODO: hflip
 760			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 761			outX = 0;
 762			int end2 = end - 4;
 763			int shift = inX & 0x3;
 764			if (end2 > 0) {
 765				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 766				tileData >>= 8 * shift;
 767				shift = 0;
 768				if (!variant) {
 769					for (; outX < end2; ++outX) {
 770						BACKGROUND_DRAW_PIXEL_256_NORMAL;
 771					}
 772				} else {
 773					for (; outX < end2; ++outX) {
 774						BACKGROUND_DRAW_PIXEL_256_VARIANT;
 775					}
 776				}
 777			}
 778
 779			tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 780			tileData >>= 8 * shift;
 781			if (!variant) {
 782				for (; outX < end; ++outX) {
 783					BACKGROUND_DRAW_PIXEL_256_NORMAL;
 784				}
 785			} else {
 786				for (; outX < end; ++outX) {
 787					BACKGROUND_DRAW_PIXEL_256_VARIANT;
 788				}
 789			}
 790		}
 791
 792		tileX = 30;
 793		BACKGROUND_TEXT_SELECT_CHARACTER;
 794		if (!background->multipalette) {
 795			charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
 796			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 797			paletteData = mapData.palette << 4;
 798			if (!mapData.hflip) {
 799				if (!variant) {
 800					for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 801						BACKGROUND_DRAW_PIXEL_16_NORMAL;
 802					}
 803				} else {
 804					for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 805						BACKGROUND_DRAW_PIXEL_16_VARIANT;
 806					}
 807				}
 808			} else {
 809				tileData >>= 4 * end;
 810				if (!variant) {
 811					for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
 812						BACKGROUND_DRAW_PIXEL_16_NORMAL;
 813					}
 814				} else {
 815					for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
 816						BACKGROUND_DRAW_PIXEL_16_VARIANT;
 817					}
 818				}
 819			}
 820		} else {
 821			// TODO: hflip
 822			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 823			outX = VIDEO_HORIZONTAL_PIXELS - 8 + end;
 824			int end2 = 4 - end;
 825			if (end2 > 0) {
 826				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 827				if (!variant) {
 828					for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
 829						BACKGROUND_DRAW_PIXEL_256_NORMAL;
 830					}
 831				} else {
 832					for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
 833						BACKGROUND_DRAW_PIXEL_256_VARIANT;
 834					}
 835				}
 836				++charBase;
 837			}
 838
 839			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 840			if (!variant) {
 841				for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 842					BACKGROUND_DRAW_PIXEL_256_NORMAL;
 843				}
 844			} else {
 845				for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 846					BACKGROUND_DRAW_PIXEL_256_VARIANT;
 847				}
 848			}
 849		}
 850
 851		tileX = 1;
 852		outX = end;
 853	}
 854
 855	if (!background->multipalette) {
 856		if (!variant) {
 857			BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
 858		 } else {
 859			BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
 860		 }
 861	} else {
 862		if (!variant) {
 863			BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
 864		 } else {
 865			BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
 866		 }
 867	}
 868}
 869
 870#define BACKGROUND_BITMAP_INIT \
 871	(void)(unused); \
 872	int32_t x = background->sx - background->dx; \
 873	int32_t y = background->sy - background->dy; \
 874	int32_t localX; \
 875	int32_t localY; \
 876	\
 877	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
 878	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
 879	flags |= FLAG_TARGET_2 * background->target2; \
 880	int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
 881
 882#define BACKGROUND_BITMAP_ITERATE(W, H) \
 883	x += background->dx; \
 884	y += background->dy; \
 885	\
 886	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
 887		continue; \
 888	} else { \
 889		localX = x; \
 890		localY = y; \
 891	}
 892
 893static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
 894	int sizeAdjusted = 0x8000 << background->size;
 895
 896	BACKGROUND_BITMAP_INIT;
 897
 898	uint32_t screenBase = background->screenBase;
 899	uint32_t charBase = background->charBase;
 900	uint8_t mapData;
 901	uint8_t tileData;
 902
 903	int outX;
 904	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 905		x += background->dx;
 906		y += background->dy;
 907
 908		if (background->overflow) {
 909			localX = x & (sizeAdjusted - 1);
 910			localY = y & (sizeAdjusted - 1);
 911		} else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
 912			continue;
 913		} else {
 914			localX = x;
 915			localY = y;
 916		}
 917		mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
 918		tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
 919
 920		if (tileData && !(renderer->row[outX] & FLAG_FINALIZED)) {
 921			if (!variant) {
 922				_composite(renderer, outX, renderer->normalPalette[tileData] | flags);
 923			} else {
 924				_composite(renderer, outX, renderer->variantPalette[tileData] | flags);
 925			}
 926		}
 927	}
 928}
 929
 930static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
 931	BACKGROUND_BITMAP_INIT;
 932
 933	uint16_t color;
 934	uint32_t color32;
 935
 936	int outX;
 937	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 938		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
 939
 940		color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
 941		color32 = 0;
 942		color32 |= (color << 3) & 0xF8;
 943		color32 |= (color << 6) & 0xF800;
 944		color32 |= (color << 9) & 0xF80000;
 945
 946		if (!(renderer->row[outX] & FLAG_FINALIZED)) {
 947			if (!variant) {
 948				_composite(renderer, outX, color32 | flags);
 949			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
 950				_composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
 951			} else if (renderer->blendEffect == BLEND_DARKEN) {
 952				_composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
 953			}
 954		}
 955	}
 956}
 957
 958static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
 959	BACKGROUND_BITMAP_INIT;
 960
 961	uint16_t color;
 962	uint32_t offset = 0;
 963	if (renderer->dispcnt.frameSelect) {
 964		offset = 0xA000;
 965	}
 966
 967	int outX;
 968	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 969		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
 970
 971		color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
 972
 973		if (color && !(renderer->row[outX] & FLAG_FINALIZED)) {
 974			if (!variant) {
 975				_composite(renderer, outX, renderer->normalPalette[color] | flags);
 976			} else {
 977				_composite(renderer, outX, renderer->variantPalette[color] | flags);
 978			}
 979		}
 980	}
 981}
 982
 983static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
 984	BACKGROUND_BITMAP_INIT;
 985
 986	uint16_t color;
 987	uint32_t color32;
 988	uint32_t offset = 0;
 989	if (renderer->dispcnt.frameSelect) {
 990		offset = 0xA000;
 991	}
 992
 993	int outX;
 994	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
 995		BACKGROUND_BITMAP_ITERATE(160, 128);
 996
 997		color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
 998		color32 = 0;
 999		color32 |= (color << 3) & 0xF8;
1000		color32 |= (color << 6) & 0xF800;
1001		color32 |= (color << 9) & 0xF80000;
1002
1003		if (!(renderer->row[outX] & FLAG_FINALIZED)) {
1004			if (!variant) {
1005				_composite(renderer, outX, color32 | flags);
1006			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1007				_composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
1008			} else if (renderer->blendEffect == BLEND_DARKEN) {
1009				_composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
1010			}
1011		}
1012	}
1013}
1014
1015static const int _objSizes[32] = {
1016	8, 8,
1017	16, 16,
1018	32, 32,
1019	64, 64,
1020	16, 8,
1021	32, 8,
1022	32, 16,
1023	64, 32,
1024	8, 16,
1025	8, 32,
1026	16, 32,
1027	32, 64,
1028	0, 0,
1029	0, 0,
1030	0, 0,
1031	0, 0
1032};
1033
1034#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1035		SPRITE_YBASE_ ## DEPTH(inY); \
1036		int outX = x >= start ? x : start; \
1037		int condition = x + width; \
1038		if (end < condition) { \
1039			condition = end; \
1040		} \
1041		for (; outX < condition; ++outX) { \
1042			int inX = outX - x; \
1043			if (sprite->hflip) { \
1044				inX = width - inX - 1; \
1045			} \
1046			if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1047				continue; \
1048			} \
1049			SPRITE_XBASE_ ## DEPTH(inX); \
1050			SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1051		}
1052
1053#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1054	for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1055		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1056			continue; \
1057		} \
1058		int inX = outX - x; \
1059		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1060		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1061		\
1062		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1063			continue; \
1064		} \
1065		\
1066		SPRITE_YBASE_ ## DEPTH(localY); \
1067		SPRITE_XBASE_ ## DEPTH(localX); \
1068		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1069	}
1070
1071#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1072#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1073
1074#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1075	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1076	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1077	if (tileData && !(renderer->spriteLayer[outX])) { \
1078		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1079	}
1080
1081#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1082	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1083	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1084	if (tileData && !(renderer->spriteLayer[outX])) { \
1085		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1086	}
1087
1088#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1089#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1090
1091#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1092	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1093	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1094	if (tileData && !(renderer->spriteLayer[outX])) { \
1095		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1096	}
1097
1098#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1099	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1100	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1101	if (tileData && !(renderer->spriteLayer[outX])) { \
1102		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1103	}
1104
1105static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1106	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1107	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1108	int start = renderer->start;
1109	int end = renderer->end;
1110	if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1111		return;
1112	}
1113	int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1114	flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1115	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1116	int x = sprite->x;
1117	int inY = y - sprite->y;
1118	if (sprite->y + height - 256 >= 0) {
1119		inY += 256;
1120	}
1121	if (sprite->vflip) {
1122		inY = height - inY - 1;
1123	}
1124	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1125	int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1126	if (!sprite->multipalette) {
1127		if (!variant) {
1128			SPRITE_NORMAL_LOOP(16, NORMAL);
1129		} else {
1130			SPRITE_NORMAL_LOOP(16, VARIANT);
1131		}
1132	} else {
1133		if (!variant) {
1134			SPRITE_NORMAL_LOOP(256, NORMAL);
1135		} else {
1136			SPRITE_NORMAL_LOOP(256, VARIANT);
1137		}
1138	}
1139}
1140
1141static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1142	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1143	int totalWidth = width << sprite->doublesize;
1144	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1145	int totalHeight = height << sprite->doublesize;
1146	int start = renderer->start;
1147	int end = renderer->end;
1148	if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1149		return;
1150	}
1151	int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1152	flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1153	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1154	int x = sprite->x;
1155	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1156	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1157	int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1158	int inY = y - sprite->y;
1159	if (inY < 0) {
1160		inY += 256;
1161	}
1162	if (!sprite->multipalette) {
1163		if (!variant) {
1164			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1165		} else {
1166			SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1167		}
1168	} else {
1169		if (!variant) {
1170			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1171		} else {
1172			SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1173		}
1174	}
1175}
1176
1177static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority) {
1178	int x;
1179	for (x = 0; x < 240; ++x) {
1180		uint32_t color = renderer->spriteLayer[x];
1181		if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(renderer->row[x] & FLAG_FINALIZED)) {
1182			_composite(renderer, x, color & ~FLAG_FINALIZED);
1183		}
1184	}
1185}
1186
1187static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1188	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1189		for (int i = 0; i < 512; ++i) {
1190			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1191		}
1192	} else if (renderer->blendEffect == BLEND_DARKEN) {
1193		for (int i = 0; i < 512; ++i) {
1194			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1195		}
1196	} else {
1197		for (int i = 0; i < 512; ++i) {
1198			renderer->variantPalette[i] = renderer->normalPalette[i];
1199		}
1200	}
1201}
1202
1203static inline uint32_t _brighten(uint32_t color, int y) {
1204	uint32_t c = 0;
1205	uint32_t a;
1206	a = color & 0xF8;
1207	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1208
1209	a = color & 0xF800;
1210	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1211
1212	a = color & 0xF80000;
1213	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1214	return c;
1215}
1216
1217static inline uint32_t _darken(uint32_t color, int y) {
1218	uint32_t c = 0;
1219	uint32_t a;
1220	a = color & 0xF8;
1221	c |= (a - (a * y) / 16) & 0xF8;
1222
1223	a = color & 0xF800;
1224	c |= (a - (a * y) / 16) & 0xF800;
1225
1226	a = color & 0xF80000;
1227	c |= (a - (a * y) / 16) & 0xF80000;
1228	return c;
1229}
1230
1231static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
1232	uint32_t c = 0;
1233	uint32_t a, b;
1234	a = colorA & 0xF8;
1235	b = colorB & 0xF8;
1236	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1237	if (c & 0x00000100) {
1238		c = 0x000000F8;
1239	}
1240
1241	a = colorA & 0xF800;
1242	b = colorB & 0xF800;
1243	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1244	if (c & 0x00010000) {
1245		c = (c & 0x000000F8) | 0x0000F800;
1246	}
1247
1248	a = colorA & 0xF80000;
1249	b = colorB & 0xF80000;
1250	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1251	if (c & 0x01000000) {
1252		c = (c & 0x0000F8F8) | 0x00F80000;
1253	}
1254	return c;
1255}