all repos — mgba @ d2f15f4af43025a918f9ab6129dbe050dbfa9df1

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