all repos — mgba @ e80ab4c855ef1d3c2e320b2a21b72b9fc1de74a4

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