all repos — mgba @ 90ddf937a5a136a9ec950b1ce6d8820f3c9a5c01

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