all repos — mgba @ 2a6e7db6932a4c78dfe8a955041593ab8570f5af

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(0, 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				outX = renderer->end - mod8;
 900				if (outX < 0) {
 901					tileData >>= 4 * -outX;
 902					outX = 0;
 903				}
 904				if (!variant) {
 905					for (; outX < renderer->end; ++outX) {
 906						BACKGROUND_DRAW_PIXEL_16_NORMAL;
 907					}
 908				} else {
 909					for (; outX < renderer->end; ++outX) {
 910						BACKGROUND_DRAW_PIXEL_16_VARIANT;
 911					}
 912				}
 913			} else {
 914				tileData >>= 4 * (0x8 - mod8);
 915				int end2 = renderer->end - 8;
 916				if (end2 < -1) {
 917					end2 = -1;
 918				}
 919				if (!variant) {
 920					for (outX = renderer->end - 1; outX > end2; --outX) {
 921						BACKGROUND_DRAW_PIXEL_16_NORMAL;
 922					}
 923				} else {
 924					for (outX = renderer->end - 1; outX > end2; --outX) {
 925						BACKGROUND_DRAW_PIXEL_16_VARIANT;
 926					}
 927				}
 928			}
 929		} else {
 930			// TODO: hflip
 931			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
 932			outX = renderer->end - 8 + end;
 933			int end2 = 4 - end;
 934			if (end2 > 0) {
 935				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 936				if (!variant) {
 937					for (; outX < renderer->end - end2; ++outX) {
 938						BACKGROUND_DRAW_PIXEL_256_NORMAL;
 939					}
 940				} else {
 941					for (; outX < renderer->end - end2; ++outX) {
 942						BACKGROUND_DRAW_PIXEL_256_VARIANT;
 943					}
 944				}
 945				++charBase;
 946			}
 947
 948			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 949			if (!variant) {
 950				for (; outX < renderer->end; ++outX) {
 951					BACKGROUND_DRAW_PIXEL_256_NORMAL;
 952				}
 953			} else {
 954				for (; outX < renderer->end; ++outX) {
 955					BACKGROUND_DRAW_PIXEL_256_VARIANT;
 956				}
 957			}
 958		}
 959
 960		tileX = (inX & 0x7) != 0;
 961		outX = renderer->start + tileX * 8 - (inX & 0x7);
 962	}
 963
 964	if (!background->multipalette) {
 965		if (!variant) {
 966			BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
 967		 } else {
 968			BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
 969		 }
 970	} else {
 971		if (!variant) {
 972			BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
 973		 } else {
 974			BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
 975		 }
 976	}
 977}
 978
 979#define BACKGROUND_BITMAP_INIT \
 980	(void)(unused); \
 981	int32_t x = background->sx - background->dx; \
 982	int32_t y = background->sy - background->dy; \
 983	int32_t localX; \
 984	int32_t localY; \
 985	\
 986	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
 987	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
 988	flags |= FLAG_TARGET_2 * background->target2; \
 989	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
 990
 991#define BACKGROUND_BITMAP_ITERATE(W, H) \
 992	x += background->dx; \
 993	y += background->dy; \
 994	\
 995	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
 996		continue; \
 997	} else { \
 998		localX = x; \
 999		localY = y; \
1000	}
1001
1002static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1003	int sizeAdjusted = 0x8000 << background->size;
1004
1005	BACKGROUND_BITMAP_INIT;
1006
1007	uint32_t screenBase = background->screenBase;
1008	uint32_t charBase = background->charBase;
1009	uint8_t mapData;
1010	uint8_t tileData;
1011
1012	int outX;
1013	for (outX = renderer->start; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1014		x += background->dx;
1015		y += background->dy;
1016
1017		if (background->overflow) {
1018			localX = x & (sizeAdjusted - 1);
1019			localY = y & (sizeAdjusted - 1);
1020		} else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
1021			continue;
1022		} else {
1023			localX = x;
1024			localY = y;
1025		}
1026		mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1027		tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1028
1029		if (tileData && !(renderer->row[outX] & FLAG_FINALIZED)) {
1030			if (!variant) {
1031				_composite(renderer, outX, renderer->normalPalette[tileData] | flags);
1032			} else {
1033				_composite(renderer, outX, renderer->variantPalette[tileData] | flags);
1034			}
1035		}
1036	}
1037}
1038
1039static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1040	BACKGROUND_BITMAP_INIT;
1041
1042	uint16_t color;
1043	uint32_t color32;
1044
1045	int outX;
1046	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1047		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1048
1049		color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1050		color32 = 0;
1051		color32 |= (color << 3) & 0xF8;
1052		color32 |= (color << 6) & 0xF800;
1053		color32 |= (color << 9) & 0xF80000;
1054
1055		if (!(renderer->row[outX] & FLAG_FINALIZED)) {
1056			if (!variant) {
1057				_composite(renderer, outX, color32 | flags);
1058			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1059				_composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
1060			} else if (renderer->blendEffect == BLEND_DARKEN) {
1061				_composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
1062			}
1063		}
1064	}
1065}
1066
1067static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1068	BACKGROUND_BITMAP_INIT;
1069
1070	uint16_t color;
1071	uint32_t offset = 0;
1072	if (renderer->dispcnt.frameSelect) {
1073		offset = 0xA000;
1074	}
1075
1076	int outX;
1077	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1078		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1079
1080		color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1081
1082		if (color && !(renderer->row[outX] & FLAG_FINALIZED)) {
1083			if (!variant) {
1084				_composite(renderer, outX, renderer->normalPalette[color] | flags);
1085			} else {
1086				_composite(renderer, outX, renderer->variantPalette[color] | flags);
1087			}
1088		}
1089	}
1090}
1091
1092static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1093	BACKGROUND_BITMAP_INIT;
1094
1095	uint16_t color;
1096	uint32_t color32;
1097	uint32_t offset = 0;
1098	if (renderer->dispcnt.frameSelect) {
1099		offset = 0xA000;
1100	}
1101
1102	int outX;
1103	for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1104		BACKGROUND_BITMAP_ITERATE(160, 128);
1105
1106		color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1107		color32 = 0;
1108		color32 |= (color << 3) & 0xF8;
1109		color32 |= (color << 6) & 0xF800;
1110		color32 |= (color << 9) & 0xF80000;
1111
1112		if (!(renderer->row[outX] & FLAG_FINALIZED)) {
1113			if (!variant) {
1114				_composite(renderer, outX, color32 | flags);
1115			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1116				_composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
1117			} else if (renderer->blendEffect == BLEND_DARKEN) {
1118				_composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
1119			}
1120		}
1121	}
1122}
1123
1124static const int _objSizes[32] = {
1125	8, 8,
1126	16, 16,
1127	32, 32,
1128	64, 64,
1129	16, 8,
1130	32, 8,
1131	32, 16,
1132	64, 32,
1133	8, 16,
1134	8, 32,
1135	16, 32,
1136	32, 64,
1137	0, 0,
1138	0, 0,
1139	0, 0,
1140	0, 0
1141};
1142
1143#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1144		SPRITE_YBASE_ ## DEPTH(inY); \
1145		int outX = x >= start ? x : start; \
1146		int condition = x + width; \
1147		if (end < condition) { \
1148			condition = end; \
1149		} \
1150		for (; outX < condition; ++outX) { \
1151			int inX = outX - x; \
1152			if (sprite->hflip) { \
1153				inX = width - inX - 1; \
1154			} \
1155			if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1156				continue; \
1157			} \
1158			SPRITE_XBASE_ ## DEPTH(inX); \
1159			SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1160		}
1161
1162#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1163	int outX; \
1164	for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1165		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1166			continue; \
1167		} \
1168		int inX = outX - x; \
1169		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1170		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1171		\
1172		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1173			continue; \
1174		} \
1175		\
1176		SPRITE_YBASE_ ## DEPTH(localY); \
1177		SPRITE_XBASE_ ## DEPTH(localX); \
1178		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1179	}
1180
1181#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1182#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1183
1184#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1185	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1186	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1187	if (tileData && !(renderer->spriteLayer[outX])) { \
1188		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1189	}
1190
1191#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1192	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1193	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1194	if (tileData && !(renderer->spriteLayer[outX])) { \
1195		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1196	}
1197
1198#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1199#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1200
1201#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1202	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1203	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1204	if (tileData && !(renderer->spriteLayer[outX])) { \
1205		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1206	}
1207
1208#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1209	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1210	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1211	if (tileData && !(renderer->spriteLayer[outX])) { \
1212		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1213	}
1214
1215static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1216	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1217	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1218	int start = renderer->start;
1219	int end = renderer->end;
1220	if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1221		return;
1222	}
1223	int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1224	flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1225	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1226	int x = sprite->x;
1227	int inY = y - sprite->y;
1228	if (sprite->y + height - 256 >= 0) {
1229		inY += 256;
1230	}
1231	if (sprite->vflip) {
1232		inY = height - inY - 1;
1233	}
1234	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1235	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1236	if (!sprite->multipalette) {
1237		if (!variant) {
1238			SPRITE_NORMAL_LOOP(16, NORMAL);
1239		} else {
1240			SPRITE_NORMAL_LOOP(16, VARIANT);
1241		}
1242	} else {
1243		if (!variant) {
1244			SPRITE_NORMAL_LOOP(256, NORMAL);
1245		} else {
1246			SPRITE_NORMAL_LOOP(256, VARIANT);
1247		}
1248	}
1249}
1250
1251static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1252	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1253	int totalWidth = width << sprite->doublesize;
1254	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1255	int totalHeight = height << sprite->doublesize;
1256	int start = renderer->start;
1257	int end = renderer->end;
1258	if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1259		return;
1260	}
1261	int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1262	flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1263	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1264	int x = sprite->x;
1265	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1266	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1267	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1268	int inY = y - sprite->y;
1269	if (inY < 0) {
1270		inY += 256;
1271	}
1272	if (!sprite->multipalette) {
1273		if (!variant) {
1274			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1275		} else {
1276			SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1277		}
1278	} else {
1279		if (!variant) {
1280			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1281		} else {
1282			SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1283		}
1284	}
1285}
1286
1287static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority) {
1288	int x;
1289	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
1290		uint32_t color = renderer->spriteLayer[x];
1291		if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(renderer->row[x] & FLAG_FINALIZED)) {
1292			_composite(renderer, x, color & ~FLAG_FINALIZED);
1293		}
1294	}
1295}
1296
1297static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1298	int i;
1299	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1300		for (i = 0; i < 512; ++i) {
1301			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1302		}
1303	} else if (renderer->blendEffect == BLEND_DARKEN) {
1304		for (i = 0; i < 512; ++i) {
1305			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1306		}
1307	} else {
1308		for (i = 0; i < 512; ++i) {
1309			renderer->variantPalette[i] = renderer->normalPalette[i];
1310		}
1311	}
1312}
1313
1314static inline uint32_t _brighten(uint32_t color, int y) {
1315	uint32_t c = 0;
1316	uint32_t a;
1317	a = color & 0xF8;
1318	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1319
1320	a = color & 0xF800;
1321	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1322
1323	a = color & 0xF80000;
1324	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1325	return c;
1326}
1327
1328static inline uint32_t _darken(uint32_t color, int y) {
1329	uint32_t c = 0;
1330	uint32_t a;
1331	a = color & 0xF8;
1332	c |= (a - (a * y) / 16) & 0xF8;
1333
1334	a = color & 0xF800;
1335	c |= (a - (a * y) / 16) & 0xF800;
1336
1337	a = color & 0xF80000;
1338	c |= (a - (a * y) / 16) & 0xF80000;
1339	return c;
1340}
1341
1342static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
1343	uint32_t c = 0;
1344	uint32_t a, b;
1345	a = colorA & 0xF8;
1346	b = colorB & 0xF8;
1347	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1348	if (c & 0x00000100) {
1349		c = 0x000000F8;
1350	}
1351
1352	a = colorA & 0xF800;
1353	b = colorB & 0xF800;
1354	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1355	if (c & 0x00010000) {
1356		c = (c & 0x000000F8) | 0x0000F800;
1357	}
1358
1359	a = colorA & 0xF80000;
1360	b = colorB & 0xF80000;
1361	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1362	if (c & 0x01000000) {
1363		c = (c & 0x0000F8F8) | 0x00F80000;
1364	}
1365	return c;
1366}