all repos — mgba @ cec9e67d8824dca8cdf3745898596441ff770672

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