all repos — mgba @ 13a2289e25dc38cb7902fcb7c47de0569e66f24c

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