all repos — mgba @ 6b86cdf9ef5369884c33a44ee7941698a6feeec4

mGBA Game Boy Advance Emulator

src/gba/renderers/video-software.c (view raw)

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