all repos — mgba @ 425056ca15ea8c378d5f86b3a6713cb339726af7

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