all repos — mgba @ ebe543e482699a068d34f53466aec433fa8ba1fc

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#ifdef NDEBUG
   7#define VIDEO_CHECKS false
   8#else
   9#define VIDEO_CHECKS true
  10#endif
  11
  12static const int _objSizes[32] = {
  13	8, 8,
  14	16, 16,
  15	32, 32,
  16	64, 64,
  17	16, 8,
  18	32, 8,
  19	32, 16,
  20	64, 32,
  21	8, 16,
  22	8, 32,
  23	16, 32,
  24	32, 64,
  25	0, 0,
  26	0, 0,
  27	0, 0,
  28	0, 0
  29};
  30
  31static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
  32static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
  33static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
  34static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
  35static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
  36static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
  37static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
  38static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels);
  39static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels);
  40
  41static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
  42static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
  43static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  44static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  45static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  46static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  47static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  48static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  49static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  50static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
  51static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
  52
  53static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
  54static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  55static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  56static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  57static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  58static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
  59static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
  60static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
  61static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
  62
  63static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
  64static inline unsigned _brighten(unsigned color, int y);
  65static inline unsigned _darken(unsigned color, int y);
  66static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);
  67
  68static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y);
  69static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win);
  70
  71void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
  72	renderer->d.init = GBAVideoSoftwareRendererInit;
  73	renderer->d.reset = GBAVideoSoftwareRendererInit;
  74	renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
  75	renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
  76	renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
  77	renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
  78	renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
  79	renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
  80	renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels;
  81	renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels;
  82}
  83
  84static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
  85	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
  86	int i;
  87
  88	softwareRenderer->dispcnt = 0x0080;
  89
  90	softwareRenderer->target1Obj = 0;
  91	softwareRenderer->target1Bd = 0;
  92	softwareRenderer->target2Obj = 0;
  93	softwareRenderer->target2Bd = 0;
  94	softwareRenderer->blendEffect = BLEND_NONE;
  95	memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
  96	memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
  97
  98	softwareRenderer->blda = 0;
  99	softwareRenderer->bldb = 0;
 100	softwareRenderer->bldy = 0;
 101
 102	softwareRenderer->winN[0] = (struct WindowN) { .control = { .priority = 0 } };
 103	softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } };
 104	softwareRenderer->objwin = (struct WindowControl) { .priority = 2 };
 105	softwareRenderer->winout = (struct WindowControl) { .priority = 3 };
 106	softwareRenderer->oamMax = 0;
 107
 108	softwareRenderer->mosaic = 0;
 109
 110	for (i = 0; i < 4; ++i) {
 111		struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
 112		bg->index = i;
 113		bg->enabled = 0;
 114		bg->priority = 0;
 115		bg->charBase = 0;
 116		bg->mosaic = 0;
 117		bg->multipalette = 0;
 118		bg->screenBase = 0;
 119		bg->overflow = 0;
 120		bg->size = 0;
 121		bg->target1 = 0;
 122		bg->target2 = 0;
 123		bg->x = 0;
 124		bg->y = 0;
 125		bg->refx = 0;
 126		bg->refy = 0;
 127		bg->dx = 256;
 128		bg->dmx = 0;
 129		bg->dy = 0;
 130		bg->dmy = 256;
 131		bg->sx = 0;
 132		bg->sy = 0;
 133	}
 134}
 135
 136static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
 137	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 138	UNUSED(softwareRenderer);
 139}
 140
 141static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 142	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 143	switch (address) {
 144	case REG_DISPCNT:
 145		softwareRenderer->dispcnt = value;
 146		GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
 147		break;
 148	case REG_BG0CNT:
 149		value &= 0xFFCF;
 150		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
 151		break;
 152	case REG_BG1CNT:
 153		value &= 0xFFCF;
 154		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
 155		break;
 156	case REG_BG2CNT:
 157		value &= 0xFFCF;
 158		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
 159		break;
 160	case REG_BG3CNT:
 161		value &= 0xFFCF;
 162		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
 163		break;
 164	case REG_BG0HOFS:
 165		value &= 0x01FF;
 166		softwareRenderer->bg[0].x = value;
 167		break;
 168	case REG_BG0VOFS:
 169		value &= 0x01FF;
 170		softwareRenderer->bg[0].y = value;
 171		break;
 172	case REG_BG1HOFS:
 173		value &= 0x01FF;
 174		softwareRenderer->bg[1].x = value;
 175		break;
 176	case REG_BG1VOFS:
 177		value &= 0x01FF;
 178		softwareRenderer->bg[1].y = value;
 179		break;
 180	case REG_BG2HOFS:
 181		value &= 0x01FF;
 182		softwareRenderer->bg[2].x = value;
 183		break;
 184	case REG_BG2VOFS:
 185		value &= 0x01FF;
 186		softwareRenderer->bg[2].y = value;
 187		break;
 188	case REG_BG3HOFS:
 189		value &= 0x01FF;
 190		softwareRenderer->bg[3].x = value;
 191		break;
 192	case REG_BG3VOFS:
 193		value &= 0x01FF;
 194		softwareRenderer->bg[3].y = value;
 195		break;
 196	case REG_BG2PA:
 197		GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
 198		break;
 199	case REG_BG2PB:
 200		GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
 201		break;
 202	case REG_BG2PC:
 203		GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
 204		break;
 205	case REG_BG2PD:
 206		GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
 207		break;
 208	case REG_BG2X_LO:
 209		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
 210		break;
 211	case REG_BG2X_HI:
 212		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
 213		break;
 214	case REG_BG2Y_LO:
 215		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
 216		break;
 217	case REG_BG2Y_HI:
 218		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
 219		break;
 220	case REG_BG3PA:
 221		GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
 222		break;
 223	case REG_BG3PB:
 224		GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
 225		break;
 226	case REG_BG3PC:
 227		GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
 228		break;
 229	case REG_BG3PD:
 230		GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
 231		break;
 232	case REG_BG3X_LO:
 233		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
 234		break;
 235	case REG_BG3X_HI:
 236		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
 237		break;
 238	case REG_BG3Y_LO:
 239		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
 240		break;
 241	case REG_BG3Y_HI:
 242		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
 243		break;
 244	case REG_BLDCNT:
 245		GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
 246		break;
 247	case REG_BLDALPHA:
 248		softwareRenderer->blda = value & 0x1F;
 249		if (softwareRenderer->blda > 0x10) {
 250			softwareRenderer->blda = 0x10;
 251		}
 252		softwareRenderer->bldb = (value >> 8) & 0x1F;
 253		if (softwareRenderer->bldb > 0x10) {
 254			softwareRenderer->bldb = 0x10;
 255		}
 256		break;
 257	case REG_BLDY:
 258		softwareRenderer->bldy = value & 0x1F;
 259		if (softwareRenderer->bldy > 0x10) {
 260			softwareRenderer->bldy = 0x10;
 261		}
 262		_updatePalettes(softwareRenderer);
 263		break;
 264	case REG_WIN0H:
 265		softwareRenderer->winN[0].h.end = value;
 266		softwareRenderer->winN[0].h.start = value >> 8;
 267		if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
 268			softwareRenderer->winN[0].h.start = 0;
 269		}
 270		if (softwareRenderer->winN[0].h.end > VIDEO_HORIZONTAL_PIXELS) {
 271			softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS;
 272			if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS) {
 273				softwareRenderer->winN[0].h.start = VIDEO_HORIZONTAL_PIXELS;
 274			}
 275		}
 276		break;
 277	case REG_WIN1H:
 278		softwareRenderer->winN[1].h.end = value;
 279		softwareRenderer->winN[1].h.start = value >> 8;
 280		if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
 281			softwareRenderer->winN[1].h.start = 0;
 282		}
 283		if (softwareRenderer->winN[1].h.end > VIDEO_HORIZONTAL_PIXELS) {
 284			softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS;
 285			if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS) {
 286				softwareRenderer->winN[1].h.start = VIDEO_HORIZONTAL_PIXELS;
 287			}
 288		}
 289		break;
 290	case REG_WIN0V:
 291		softwareRenderer->winN[0].v.end = value;
 292		softwareRenderer->winN[0].v.start = value >> 8;
 293		if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
 294			softwareRenderer->winN[0].v.start = 0;
 295		}
 296		if (softwareRenderer->winN[0].v.end > VIDEO_VERTICAL_PIXELS) {
 297			softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
 298			if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS) {
 299				softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS;
 300			}
 301		}
 302		break;
 303	case REG_WIN1V:
 304		softwareRenderer->winN[1].v.end = value;
 305		softwareRenderer->winN[1].v.start = value >> 8;
 306		if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
 307			softwareRenderer->winN[1].v.start = 0;
 308		}
 309		if (softwareRenderer->winN[1].v.end > VIDEO_VERTICAL_PIXELS) {
 310			softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
 311			if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS) {
 312				softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS;
 313			}
 314		}
 315		break;
 316	case REG_WININ:
 317		softwareRenderer->winN[0].control.packed = value;
 318		softwareRenderer->winN[1].control.packed = value >> 8;
 319		break;
 320	case REG_WINOUT:
 321		softwareRenderer->winout.packed = value;
 322		softwareRenderer->objwin.packed = value >> 8;
 323		break;
 324	case REG_MOSAIC:
 325		softwareRenderer->mosaic = value;
 326		break;
 327	case REG_GREENSWP:
 328		GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);
 329		break;
 330	default:
 331		GBALog(0, GBA_LOG_GAME_ERROR, "Invalid video register: 0x%03X", address);
 332	}
 333	return value;
 334}
 335
 336static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
 337	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 338	softwareRenderer->oamDirty = 1;
 339	UNUSED(oam);
 340}
 341
 342static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 343	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 344#ifdef COLOR_16_BIT
 345#ifdef COLOR_5_6_5
 346	unsigned color = 0;
 347	color |= (value & 0x001F) << 11;
 348	color |= (value & 0x03E0) << 1;
 349	color |= (value & 0x7C00) >> 10;
 350#else
 351	unsigned color = value;
 352#endif
 353#else
 354	unsigned color = 0;
 355	color |= (value << 3) & 0xF8;
 356	color |= (value << 6) & 0xF800;
 357	color |= (value << 9) & 0xF80000;
 358#endif
 359	softwareRenderer->normalPalette[address >> 1] = color;
 360	if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
 361		softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
 362	} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
 363		softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
 364	}
 365}
 366
 367static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {
 368	if (win->v.end >= win->v.start) {
 369		if (y >= win->v.end) {
 370			return;
 371		}
 372		if (y < win->v.start) {
 373			return;
 374		}
 375	} else if (y >= win->v.end && y < win->v.start) {
 376		return;
 377	}
 378	if (win->h.end > VIDEO_HORIZONTAL_PIXELS || win->h.end < win->h.start) {
 379		struct WindowN splits[2] = { *win, *win };
 380		splits[0].h.start = 0;
 381		splits[1].h.end = VIDEO_HORIZONTAL_PIXELS;
 382		_breakWindowInner(softwareRenderer, &splits[0]);
 383		_breakWindowInner(softwareRenderer, &splits[1]);
 384	} else {
 385		_breakWindowInner(softwareRenderer, win);
 386	}
 387}
 388
 389static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
 390	int activeWindow;
 391	int startX = 0;
 392	if (win->h.end > 0) {
 393		for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
 394			if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
 395				// Insert a window before the end of the active window
 396				struct Window oldWindow = softwareRenderer->windows[activeWindow];
 397				if (win->h.start > startX) {
 398					// And after the start of the active window
 399					int nextWindow = softwareRenderer->nWindows;
 400					++softwareRenderer->nWindows;
 401					for (; nextWindow > activeWindow; --nextWindow) {
 402						softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
 403					}
 404					softwareRenderer->windows[activeWindow].endX = win->h.start;
 405					++activeWindow;
 406				}
 407				softwareRenderer->windows[activeWindow].control = win->control;
 408				softwareRenderer->windows[activeWindow].endX = win->h.end;
 409				if (win->h.end >= oldWindow.endX) {
 410					// Trim off extra windows we've overwritten
 411					for (++activeWindow; softwareRenderer->nWindows > activeWindow + 1 && win->h.end >= softwareRenderer->windows[activeWindow].endX; ++activeWindow) {
 412#ifdef DEBUG
 413						if (activeWindow >= MAX_WINDOW) {
 414							GBALog(0, GBA_LOG_DANGER, "Out of bounds window write will occur");
 415							return;
 416						}
 417#endif
 418						softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
 419						--softwareRenderer->nWindows;
 420					}
 421				} else {
 422					++activeWindow;
 423					int nextWindow = softwareRenderer->nWindows;
 424					++softwareRenderer->nWindows;
 425					for (; nextWindow > activeWindow; --nextWindow) {
 426						softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
 427					}
 428					softwareRenderer->windows[activeWindow] = oldWindow;
 429				}
 430				break;
 431			}
 432			startX = softwareRenderer->windows[activeWindow].endX;
 433		}
 434	}
 435#ifdef DEBUG
 436	if (softwareRenderer->nWindows > MAX_WINDOW) {
 437		GBALog(0, GBA_LOG_ABORT, "Out of bounds window write occurred!");
 438	}
 439#endif
 440}
 441
 442static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
 443	int i;
 444	int oamMax = 0;
 445	for (i = 0; i < 128; ++i) {
 446		struct GBAObj obj;
 447		LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a);
 448		LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b);
 449		LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c);
 450		if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
 451			int height = _objSizes[GBAObjAttributesAGetShape(obj.a) * 8 + GBAObjAttributesBGetSize(obj.b) * 2 + 1];
 452			if (GBAObjAttributesAIsTransformed(obj.a)) {
 453				height <<= GBAObjAttributesAGetDoubleSize(obj.a);
 454			}
 455			if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
 456				renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a);
 457				renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height;
 458				renderer->sprites[oamMax].obj = obj;
 459				++oamMax;
 460			}
 461		}
 462	}
 463	renderer->oamMax = oamMax;
 464	renderer->oamDirty = 0;
 465}
 466
 467
 468static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
 469	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 470
 471	color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
 472	if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
 473		int x;
 474		for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 475			row[x] = GBA_COLOR_WHITE;
 476		}
 477		return;
 478	}
 479
 480	int x;
 481	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; x += 4) {
 482		softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
 483		softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
 484		softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
 485		softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
 486	}
 487
 488	softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
 489	softwareRenderer->nWindows = 1;
 490	if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
 491		softwareRenderer->windows[0].control = softwareRenderer->winout;
 492		if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt)) {
 493			_breakWindow(softwareRenderer, &softwareRenderer->winN[1], y);
 494		}
 495		if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt)) {
 496			_breakWindow(softwareRenderer, &softwareRenderer->winN[0], y);
 497		}
 498	} else {
 499		softwareRenderer->windows[0].control.packed = 0xFF;
 500	}
 501
 502	int w;
 503	x = 0;
 504	for (w = 0; w < softwareRenderer->nWindows; ++w) {
 505		// TOOD: handle objwin on backdrop
 506		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
 507		if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
 508			backdrop |= softwareRenderer->normalPalette[0];
 509		} else {
 510			backdrop |= softwareRenderer->variantPalette[0];
 511		}
 512		int end = softwareRenderer->windows[w].endX;
 513		for (; x < end; ++x) {
 514			softwareRenderer->row[x] = backdrop;
 515		}
 516	}
 517
 518	_drawScanline(softwareRenderer, y);
 519
 520	if (softwareRenderer->target2Bd) {
 521		x = 0;
 522		for (w = 0; w < softwareRenderer->nWindows; ++w) {
 523		uint32_t backdrop = FLAG_UNWRITTEN;
 524			if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
 525				backdrop |= softwareRenderer->normalPalette[0];
 526			} else {
 527				backdrop |= softwareRenderer->variantPalette[0];
 528			}
 529			int end = softwareRenderer->windows[w].endX;
 530			for (; x < end; ++x) {
 531				uint32_t color = softwareRenderer->row[x];
 532				if (color & FLAG_TARGET_1) {
 533					softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
 534				}
 535			}
 536		}
 537	}
 538
 539#ifdef COLOR_16_BIT
 540#ifdef __arm__
 541	_to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
 542#else
 543	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 544		row[x] = softwareRenderer->row[x];
 545	}
 546#endif
 547#else
 548	memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
 549#endif
 550}
 551
 552static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
 553	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 554
 555	softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
 556	softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
 557	softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
 558	softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
 559}
 560
 561static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
 562	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 563
 564	*stride = softwareRenderer->outputBufferStride;
 565	*pixels = softwareRenderer->outputBuffer;
 566}
 567
 568static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
 569	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 570
 571	uint32_t* colorPixels = pixels;
 572	unsigned i;
 573	for (i = 0; i < VIDEO_VERTICAL_PIXELS; ++i) {
 574		memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
 575	}
 576}
 577
 578static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
 579	renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt);
 580	renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt);
 581	renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt);
 582	renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt);
 583}
 584
 585static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 586	UNUSED(renderer);
 587	bg->priority = GBARegisterBGCNTGetPriority(value);
 588	bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14;
 589	bg->mosaic = GBARegisterBGCNTGetMosaic(value);
 590	bg->multipalette = GBARegisterBGCNTGet256Color(value);
 591	bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 11;
 592	bg->overflow = GBARegisterBGCNTGetOverflow(value);
 593	bg->size = GBARegisterBGCNTGetSize(value);
 594}
 595
 596static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 597	bg->dx = value;
 598}
 599
 600static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 601	bg->dmx = value;
 602}
 603
 604static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 605	bg->dy = value;
 606}
 607
 608static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 609	bg->dmy = value;
 610}
 611
 612static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 613	bg->refx = (bg->refx & 0xFFFF0000) | value;
 614	bg->sx = bg->refx;
 615}
 616
 617static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 618	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
 619	bg->refx <<= 4;
 620	bg->refx >>= 4;
 621	bg->sx = bg->refx;
 622}
 623
 624static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 625	bg->refy = (bg->refy & 0xFFFF0000) | value;
 626	bg->sy = bg->refy;
 627}
 628
 629static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 630	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
 631	bg->refy <<= 4;
 632	bg->refy >>= 4;
 633	bg->sy = bg->refy;
 634}
 635
 636static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
 637	enum BlendEffect oldEffect = renderer->blendEffect;
 638
 639	renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
 640	renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
 641	renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
 642	renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
 643	renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
 644	renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
 645	renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
 646	renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
 647
 648	renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
 649	renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
 650	renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
 651	renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
 652	renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
 653
 654	renderer->anyTarget2 = value & 0x3F00;
 655
 656	if (oldEffect != renderer->blendEffect) {
 657		_updatePalettes(renderer);
 658	}
 659}
 660
 661#define TEST_LAYER_ENABLED(X) \
 662	(renderer->bg[X].enabled && \
 663	(GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \
 664	(GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \
 665	renderer->bg[X].priority == priority)
 666
 667static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
 668	int w;
 669	renderer->end = 0;
 670	int spriteLayers = 0;
 671	if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt)) {
 672		if (renderer->oamDirty) {
 673			_cleanOAM(renderer);
 674		}
 675		int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
 676		int mosaicY = y - (y % mosaicV);
 677		for (w = 0; w < renderer->nWindows; ++w) {
 678			renderer->start = renderer->end;
 679			renderer->end = renderer->windows[w].endX;
 680			renderer->currentWindow = renderer->windows[w].control;
 681			if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
 682				continue;
 683			}
 684			int i;
 685			int drawn;
 686			for (i = 0; i < renderer->oamMax; ++i) {
 687				int localY = y;
 688				struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
 689				if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
 690					localY = mosaicY;
 691				}
 692				if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
 693					continue;
 694				}
 695				drawn = _preprocessSprite(renderer, &sprite->obj, localY);
 696				spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
 697			}
 698		}
 699	}
 700
 701	int priority;
 702	for (priority = 0; priority < 4; ++priority) {
 703		renderer->end = 0;
 704		for (w = 0; w < renderer->nWindows; ++w) {
 705			renderer->start = renderer->end;
 706			renderer->end = renderer->windows[w].endX;
 707			renderer->currentWindow = renderer->windows[w].control;
 708			if (spriteLayers & (1 << priority)) {
 709				_postprocessSprite(renderer, priority);
 710			}
 711			if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
 712				_drawBackgroundMode0(renderer, &renderer->bg[0], y);
 713			}
 714			if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
 715				_drawBackgroundMode0(renderer, &renderer->bg[1], y);
 716			}
 717			if (TEST_LAYER_ENABLED(2)) {
 718				switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
 719				case 0:
 720					_drawBackgroundMode0(renderer, &renderer->bg[2], y);
 721					break;
 722				case 1:
 723				case 2:
 724					_drawBackgroundMode2(renderer, &renderer->bg[2], y);
 725					break;
 726				case 3:
 727					_drawBackgroundMode3(renderer, &renderer->bg[2], y);
 728					break;
 729				case 4:
 730					_drawBackgroundMode4(renderer, &renderer->bg[2], y);
 731					break;
 732				case 5:
 733					_drawBackgroundMode5(renderer, &renderer->bg[2], y);
 734					break;
 735				}
 736			}
 737			if (TEST_LAYER_ENABLED(3)) {
 738				switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
 739				case 0:
 740					_drawBackgroundMode0(renderer, &renderer->bg[3], y);
 741					break;
 742				case 2:
 743					_drawBackgroundMode2(renderer, &renderer->bg[3], y);
 744					break;
 745				}
 746			}
 747		}
 748	}
 749	renderer->bg[2].sx += renderer->bg[2].dmx;
 750	renderer->bg[2].sy += renderer->bg[2].dmy;
 751	renderer->bg[3].sx += renderer->bg[3].dmx;
 752	renderer->bg[3].sy += renderer->bg[3].dmy;
 753}
 754
 755// We stash the priority on the top bits so we can do a one-operator comparison
 756// The lower the number, the higher the priority, and sprites take precendence over backgrounds
 757// We want to do special processing if the color pixel is target 1, however
 758
 759static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 760	if (color >= current) {
 761		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 762			color = _mix(renderer->blda, current, renderer->bldb, color);
 763		} else {
 764			color = current & 0x00FFFFFF;
 765		}
 766	} else {
 767		color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
 768	}
 769	*pixel = color;
 770}
 771
 772static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 773	// We stash the priority on the top bits so we can do a one-operator comparison
 774	// The lower the number, the higher the priority, and sprites take precendence over backgrounds
 775	// We want to do special processing if the color pixel is target 1, however
 776	if (color >= current) {
 777		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 778			color = _mix(renderer->blda, current, renderer->bldb, color);
 779		} else {
 780			color = current & 0x00FFFFFF;
 781		}
 782	} else {
 783		color = color & ~FLAG_TARGET_2;
 784	}
 785	*pixel = color;
 786}
 787
 788static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 789	UNUSED(renderer);
 790	if (color < current) {
 791		*pixel = color | (current & FLAG_OBJWIN);
 792	}
 793}
 794
 795static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 796	UNUSED(renderer);
 797	if (color < current) {
 798		*pixel = color;
 799	}
 800}
 801
 802#define COMPOSITE_16_OBJWIN(BLEND) \
 803	if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
 804		unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
 805		_composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
 806	}
 807
 808#define COMPOSITE_16_NO_OBJWIN(BLEND) \
 809	_composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
 810
 811#define COMPOSITE_256_OBJWIN(BLEND) \
 812	if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
 813		unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
 814		_composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
 815	}
 816
 817#define COMPOSITE_256_NO_OBJWIN(BLEND) \
 818	COMPOSITE_16_NO_OBJWIN(BLEND)
 819
 820#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \
 821	pixelData = tileData & 0xF; \
 822	current = *pixel; \
 823	if (pixelData && IS_WRITABLE(current)) { \
 824		COMPOSITE_16_ ## OBJWIN (BLEND); \
 825	} \
 826	tileData >>= 4;
 827
 828#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN) \
 829	pixelData = tileData & 0xFF; \
 830	current = *pixel; \
 831	if (pixelData && IS_WRITABLE(current)) { \
 832		COMPOSITE_256_ ## OBJWIN (BLEND); \
 833	} \
 834	tileData >>= 8;
 835
 836#define BACKGROUND_TEXT_SELECT_CHARACTER \
 837	localX = tileX * 8 + inX; \
 838	xBase = localX & 0xF8; \
 839	if (background->size & 1) { \
 840		xBase += (localX & 0x100) << 5; \
 841	} \
 842	screenBase = yBase + (xBase >> 3); \
 843	LOAD_16(mapData, screenBase << 1, vram); \
 844	localY = inY & 0x7; \
 845	if (GBA_TEXT_MAP_VFLIP(mapData)) { \
 846		localY = 7 - localY; \
 847	}
 848
 849#define PREPARE_OBJWIN \
 850	int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \
 851	int objwinOnly = 0; \
 852	int objwinForceEnable = 0; \
 853	color_t* objwinPalette; \
 854	if (objwinSlowPath) { \
 855		if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
 856			objwinPalette = renderer->variantPalette; \
 857		} else { \
 858			objwinPalette = renderer->normalPalette; \
 859		} \
 860		switch (background->index) { \
 861		case 0: \
 862			objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
 863			objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
 864			break; \
 865		case 1: \
 866			objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
 867			objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
 868			break; \
 869		case 2: \
 870			objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
 871			objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
 872			break; \
 873		case 3: \
 874			objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
 875			objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
 876			break; \
 877		} \
 878	}
 879
 880#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
 881	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 882	palette = &mainPalette[paletteData]; \
 883	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 884	LOAD_32(tileData, charBase, vram); \
 885	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 886		tileData >>= 4 * mod8; \
 887		for (; outX < end; ++outX, ++pixel) { \
 888			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 889		} \
 890	} else { \
 891		for (outX = end - 1; outX >= renderer->start; --outX) { \
 892			uint32_t* pixel = &renderer->row[outX]; \
 893			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 894		} \
 895	}
 896
 897#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
 898	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 899	LOAD_32(tileData, charBase, vram); \
 900	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 901	palette = &mainPalette[paletteData]; \
 902	pixel = &renderer->row[outX]; \
 903	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 904		if (outX < renderer->start) { \
 905			tileData >>= 4 * (renderer->start - outX); \
 906			outX = renderer->start; \
 907			pixel = &renderer->row[outX]; \
 908		} \
 909		for (; outX < renderer->end; ++outX, ++pixel) { \
 910			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 911		} \
 912	} else { \
 913		tileData >>= 4 * (0x8 - mod8); \
 914		int end = renderer->end - 8; \
 915		if (end < -1) { \
 916			end = -1; \
 917		} \
 918		outX = renderer->end - 1; \
 919		pixel = &renderer->row[outX]; \
 920		for (; outX > end; --outX, --pixel) { \
 921			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 922		} \
 923		/* Needed for consistency checks */ \
 924		if (VIDEO_CHECKS) { \
 925			outX = renderer->end; \
 926			pixel = &renderer->row[outX]; \
 927		} \
 928	}
 929
 930#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
 931	for (; tileX < tileEnd; ++tileX) { \
 932		BACKGROUND_TEXT_SELECT_CHARACTER; \
 933		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 934		tileData = carryData; \
 935		for (x = 0; x < 8; ++x) { \
 936			if (!mosaicWait) { \
 937				paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 938				palette = &mainPalette[paletteData]; \
 939				LOAD_32(tileData, charBase, vram); \
 940				if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 941					tileData >>= x * 4; \
 942				} else { \
 943					tileData >>= (7 - x) * 4; \
 944				} \
 945				tileData &= 0xF; \
 946				tileData |= tileData << 4; \
 947				tileData |= tileData << 8; \
 948				tileData |= tileData << 12; \
 949				tileData |= tileData << 16; \
 950				tileData |= tileData << 20; \
 951				tileData |= tileData << 24; \
 952				tileData |= tileData << 28; \
 953				carryData = tileData; \
 954				mosaicWait = mosaicH; \
 955			} \
 956			--mosaicWait; \
 957			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 958			++pixel; \
 959		} \
 960	}
 961
 962#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
 963	for (; tileX < tileEnd; ++tileX) { \
 964		BACKGROUND_TEXT_SELECT_CHARACTER; \
 965		paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 966		palette = &mainPalette[paletteData]; \
 967		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 968		LOAD_32(tileData, charBase, vram); \
 969		if (tileData) { \
 970			if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 971				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 972				++pixel; \
 973				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 974				++pixel; \
 975				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 976				++pixel; \
 977				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 978				++pixel; \
 979				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 980				++pixel; \
 981				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 982				++pixel; \
 983				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 984				++pixel; \
 985				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 986				++pixel; \
 987			} else { \
 988				pixel += 7; \
 989				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 990				--pixel; \
 991				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 992				--pixel; \
 993				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 994				--pixel; \
 995				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 996				--pixel; \
 997				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
 998				--pixel; \
 999				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1000				--pixel; \
1001				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1002				--pixel; \
1003				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
1004				pixel += 8; \
1005			} \
1006		} else { \
1007			pixel += 8; \
1008		} \
1009	}
1010
1011#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
1012	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1013	int end2 = end - 4; \
1014	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1015		int shift = inX & 0x3; \
1016		if (end2 > outX) { \
1017			LOAD_32(tileData, charBase, vram); \
1018			tileData >>= 8 * shift; \
1019			shift = 0; \
1020			for (; outX < end2; ++outX, ++pixel) { \
1021				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1022			} \
1023		} \
1024		\
1025		LOAD_32(tileData, charBase + 4, vram); \
1026		tileData >>= 8 * shift; \
1027		for (; outX < end; ++outX, ++pixel) { \
1028			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1029		} \
1030	} else { \
1031		int start = outX; \
1032		outX = end - 1; \
1033		pixel = &renderer->row[outX]; \
1034		if (end2 > start) { \
1035			LOAD_32(tileData, charBase, vram); \
1036			for (; outX >= end2; --outX, --pixel) { \
1037				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1038			} \
1039			charBase += 4; \
1040		} \
1041		\
1042		LOAD_32(tileData, charBase, vram); \
1043		for (; outX >= renderer->start; --outX, --pixel) { \
1044			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1045		} \
1046		outX = end; \
1047		pixel = &renderer->row[outX]; \
1048	}
1049
1050#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
1051	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1052	int end = mod8 - 4; \
1053	pixel = &renderer->row[outX]; \
1054	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1055		if (end > 0) { \
1056			LOAD_32(tileData, charBase, vram); \
1057			for (; outX < renderer->end - end; ++outX, ++pixel) { \
1058				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1059			} \
1060			charBase += 4; \
1061		} \
1062		\
1063		LOAD_32(tileData, charBase, vram); \
1064		for (; outX < renderer->end; ++outX, ++pixel) { \
1065			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1066		} \
1067	} else { \
1068		int shift = (8 - mod8) & 0x3; \
1069		int start = outX; \
1070		outX = renderer->end - 1; \
1071		pixel = &renderer->row[outX]; \
1072		if (end > 0) { \
1073			LOAD_32(tileData, charBase, vram); \
1074			tileData >>= 8 * shift; \
1075			for (; outX >= start + 4; --outX, --pixel) { \
1076				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1077			} \
1078			shift = 0; \
1079		} \
1080		\
1081		LOAD_32(tileData, charBase + 4, vram); \
1082		tileData >>= 8 * shift; \
1083		for (; outX >= start; --outX, --pixel) { \
1084			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1085		} \
1086		/* Needed for consistency checks */ \
1087		if (VIDEO_CHECKS) { \
1088			outX = renderer->end; \
1089			pixel = &renderer->row[outX]; \
1090		} \
1091	}
1092
1093#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
1094	for (; tileX < tileEnd; ++tileX) { \
1095		BACKGROUND_TEXT_SELECT_CHARACTER; \
1096		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1097		if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1098			LOAD_32(tileData, charBase, vram); \
1099			if (tileData) { \
1100					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1101					++pixel; \
1102					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1103					++pixel; \
1104					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1105					++pixel; \
1106					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1107					++pixel; \
1108			} else { \
1109				pixel += 4; \
1110			} \
1111			LOAD_32(tileData, charBase + 4, vram); \
1112			if (tileData) { \
1113					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1114					++pixel; \
1115					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1116					++pixel; \
1117					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1118					++pixel; \
1119					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1120					++pixel; \
1121			} else { \
1122				pixel += 4; \
1123			} \
1124		} else { \
1125			LOAD_32(tileData, charBase + 4, vram); \
1126			if (tileData) { \
1127				pixel += 3; \
1128				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1129				--pixel; \
1130				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1131				--pixel; \
1132				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1133				--pixel; \
1134				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1135			} \
1136			pixel += 4; \
1137			LOAD_32(tileData, charBase, vram); \
1138			if (tileData) { \
1139				pixel += 3; \
1140				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1141				--pixel; \
1142				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1143				--pixel; \
1144				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1145				--pixel; \
1146				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1147			} \
1148			pixel += 4; \
1149		} \
1150	}
1151
1152#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
1153	for (; tileX < tileEnd; ++tileX) { \
1154		BACKGROUND_TEXT_SELECT_CHARACTER; \
1155		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1156		tileData = carryData; \
1157		for (x = 0; x < 8; ++x) { \
1158			if (!mosaicWait) { \
1159				if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1160					if (x >= 4) { \
1161						LOAD_32(tileData, charBase + 4, vram); \
1162						tileData >>= (x - 4) * 8; \
1163					} else { \
1164						LOAD_32(tileData, charBase, vram); \
1165						tileData >>= x * 8; \
1166					} \
1167				} else { \
1168					if (x >= 4) { \
1169						LOAD_32(tileData, charBase, vram); \
1170						tileData >>= (7 - x) * 8; \
1171					} else { \
1172						LOAD_32(tileData, charBase + 4, vram); \
1173						tileData >>= (3 - x) * 8; \
1174					} \
1175				} \
1176				tileData &= 0xFF; \
1177				carryData = tileData; \
1178				mosaicWait = mosaicH; \
1179			} \
1180			tileData |= tileData << 8; \
1181			--mosaicWait; \
1182			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1183			++pixel; \
1184		} \
1185	}
1186
1187#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
1188	uint32_t* pixel = &renderer->row[outX]; \
1189	if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
1190		int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
1191		int x; \
1192		int mosaicWait = outX % mosaicH; \
1193		int carryData = 0; \
1194		paletteData = 0; /* Quiets compiler warning */ \
1195		DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
1196		return; \
1197	} \
1198	\
1199	if (inX & 0x7) { \
1200		BACKGROUND_TEXT_SELECT_CHARACTER; \
1201		\
1202		int mod8 = inX & 0x7; \
1203		int end = outX + 0x8 - mod8; \
1204		if (end > renderer->end) { \
1205			end = renderer->end; \
1206		} \
1207		if (UNLIKELY(end == outX)) { \
1208			return; \
1209		} \
1210		if (UNLIKELY(end < outX)) { \
1211			GBALog(0, GBA_LOG_DANGER, "Out of bounds background draw!"); \
1212			return; \
1213		} \
1214		DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
1215		outX = end; \
1216		if (tileX < tileEnd) { \
1217			++tileX; \
1218		} else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
1219			GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
1220			return; \
1221		} \
1222		length -= end - renderer->start; \
1223	} \
1224	/*! TODO: Make sure these lines can be removed */ \
1225	/*!*/ pixel = &renderer->row[outX]; \
1226	outX += (tileEnd - tileX) * 8; \
1227	/*!*/ if (VIDEO_CHECKS &&  UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
1228	/*!*/	GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw would occur!"); \
1229	/*!*/	return; \
1230	/*!*/ } \
1231	DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
1232	if (length & 0x7) { \
1233		BACKGROUND_TEXT_SELECT_CHARACTER; \
1234		\
1235		int mod8 = length & 0x7; \
1236		if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
1237			GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw!"); \
1238			return; \
1239		} \
1240		DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
1241	} \
1242	if (VIDEO_CHECKS && UNLIKELY(&renderer->row[outX] != pixel)) { \
1243		GBALog(0, GBA_LOG_FATAL, "Background draw ended in the wrong place! Diff: %" PRIXPTR, &renderer->row[outX] - pixel); \
1244	} \
1245	if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
1246		GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw occurred!"); \
1247		return; \
1248	}
1249
1250static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
1251	int inX = renderer->start + background->x;
1252	int length = renderer->end - renderer->start;
1253	if (background->mosaic) {
1254		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
1255		y -= y % mosaicV;
1256	}
1257	int inY = y + background->y;
1258	uint16_t mapData;
1259
1260	unsigned yBase = inY & 0xF8;
1261	if (background->size == 2) {
1262		yBase += inY & 0x100;
1263	} else if (background->size == 3) {
1264		yBase += (inY & 0x100) << 1;
1265	}
1266	yBase = (background->screenBase >> 1) + (yBase << 2);
1267
1268	int localX;
1269	int localY;
1270
1271	unsigned xBase;
1272
1273	int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
1274	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
1275	flags |= FLAG_TARGET_2 * background->target2;
1276	if (renderer->blda == 0x10 && renderer->bldb == 0) {
1277		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
1278	}
1279
1280	uint32_t screenBase;
1281	uint32_t charBase;
1282	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1283	color_t* mainPalette = renderer->normalPalette;
1284	if (variant) {
1285		mainPalette = renderer->variantPalette;
1286	}
1287	color_t* palette = mainPalette;
1288	PREPARE_OBJWIN;
1289
1290	int outX = renderer->start;
1291
1292	uint32_t tileData;
1293	uint32_t current;
1294	int pixelData;
1295	int paletteData;
1296	int tileX = 0;
1297	int tileEnd = ((length + inX) >> 3) - (inX >> 3);
1298	uint16_t* vram = renderer->d.vram;
1299
1300	if (!objwinSlowPath) {
1301		if (!(flags & FLAG_TARGET_2)) {
1302			if (!background->multipalette) {
1303				DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
1304			} else {
1305				DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
1306			}
1307		} else {
1308			if (!background->multipalette) {
1309				DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
1310			} else {
1311				DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
1312			}
1313		}
1314	} else {
1315		if (!(flags & FLAG_TARGET_2)) {
1316			if (!background->multipalette) {
1317				DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
1318			} else {
1319				DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
1320			}
1321		} else {
1322			if (!background->multipalette) {
1323				DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
1324			} else {
1325				DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
1326			}
1327		}
1328	}
1329}
1330
1331#define BACKGROUND_BITMAP_INIT \
1332	int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1333	int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1334	int mosaicH = 0; \
1335	int mosaicWait = 0; \
1336	if (background->mosaic) { \
1337		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; \
1338		y -= (inY % mosaicV) * background->dmy; \
1339		x -= (inY % mosaicV) * background->dmx; \
1340		mosaicH = GBAMosaicControlGetBgH(renderer->mosaic); \
1341		mosaicWait = renderer->start % (mosaicH + 1); \
1342	} \
1343	int32_t localX; \
1344	int32_t localY; \
1345	\
1346	int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
1347	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1348	flags |= FLAG_TARGET_2 * background->target2; \
1349	if (renderer->blda == 0x10 && renderer->bldb == 0) { \
1350		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \
1351	} \
1352	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1353	color_t* palette = renderer->normalPalette; \
1354	if (variant) { \
1355		palette = renderer->variantPalette; \
1356	} \
1357	PREPARE_OBJWIN;
1358
1359#define BACKGROUND_BITMAP_ITERATE(W, H) \
1360	x += background->dx; \
1361	y += background->dy; \
1362	\
1363	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1364		continue; \
1365	} else { \
1366		localX = x; \
1367		localY = y; \
1368	}
1369
1370static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1371	int sizeAdjusted = 0x8000 << background->size;
1372
1373	BACKGROUND_BITMAP_INIT;
1374
1375	uint32_t screenBase = background->screenBase;
1376	uint32_t charBase = background->charBase;
1377	uint8_t mapData;
1378	uint8_t tileData = 0;
1379
1380	int outX;
1381	uint32_t* pixel;
1382	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1383		x += background->dx;
1384		y += background->dy;
1385
1386		if (!mosaicWait) {
1387			if (background->overflow) {
1388				localX = x & (sizeAdjusted - 1);
1389				localY = y & (sizeAdjusted - 1);
1390			} else if ((x | y) & ~(sizeAdjusted - 1)) {
1391				continue;
1392			} else {
1393				localX = x;
1394				localY = y;
1395			}
1396			mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1397			tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1398
1399			mosaicWait = mosaicH;
1400		} else {
1401			--mosaicWait;
1402		}
1403
1404		uint32_t current = *pixel;
1405		if (tileData && IS_WRITABLE(current)) {
1406			if (!objwinSlowPath) {
1407				_compositeBlendNoObjwin(renderer, pixel, palette[tileData] | flags, current);
1408			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1409				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1410				_compositeBlendObjwin(renderer, pixel, currentPalette[tileData] | flags, current);
1411			}
1412		}
1413	}
1414}
1415
1416static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1417	BACKGROUND_BITMAP_INIT;
1418
1419	uint32_t color = renderer->normalPalette[0];
1420
1421	int outX;
1422	uint32_t* pixel;
1423	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1424		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1425
1426		if (!mosaicWait) {
1427			LOAD_16(color, ((localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
1428#ifndef COLOR_16_BIT
1429			unsigned color32;
1430			color32 = 0;
1431			color32 |= (color << 3) & 0xF8;
1432			color32 |= (color << 6) & 0xF800;
1433			color32 |= (color << 9) & 0xF80000;
1434			color = color32;
1435#endif
1436			mosaicWait = mosaicH;
1437		} else {
1438			--mosaicWait;
1439		}
1440
1441		uint32_t current = *pixel;
1442		if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1443			if (!variant) {
1444				_compositeBlendObjwin(renderer, pixel, color | flags, current);
1445			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1446				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1447			} else if (renderer->blendEffect == BLEND_DARKEN) {
1448				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1449			}
1450		}
1451	}
1452}
1453
1454static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1455	BACKGROUND_BITMAP_INIT;
1456
1457	uint16_t color = renderer->normalPalette[0];
1458	uint32_t offset = 0;
1459	if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1460		offset = 0xA000;
1461	}
1462
1463	int outX;
1464	uint32_t* pixel;
1465	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1466		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1467
1468		if (!mosaicWait) {
1469			color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1470
1471			mosaicWait = mosaicH;
1472		} else {
1473			--mosaicWait;
1474		}
1475
1476		uint32_t current = *pixel;
1477		if (color && IS_WRITABLE(current)) {
1478			if (!objwinSlowPath) {
1479				_compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
1480			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1481				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1482				_compositeBlendObjwin(renderer, pixel, currentPalette[color] | flags, current);
1483			}
1484		}
1485	}
1486}
1487
1488static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
1489	BACKGROUND_BITMAP_INIT;
1490
1491	uint32_t color = renderer->normalPalette[0];
1492	uint32_t offset = 0;
1493	if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1494		offset = 0xA000;
1495	}
1496
1497	int outX;
1498	uint32_t* pixel;
1499	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1500		BACKGROUND_BITMAP_ITERATE(160, 128);
1501
1502		if (!mosaicWait) {
1503			LOAD_16(color, (offset + (localX >> 8) + (localY >> 8) * 160) << 1, renderer->d.vram);
1504#ifndef COLOR_16_BIT
1505			unsigned color32 = 0;
1506			color32 |= (color << 9) & 0xF80000;
1507			color32 |= (color << 3) & 0xF8;
1508			color32 |= (color << 6) & 0xF800;
1509			color = color32;
1510#endif
1511			mosaicWait = mosaicH;
1512		} else {
1513			--mosaicWait;
1514		}
1515
1516		uint32_t current = *pixel;
1517		if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1518			if (!variant) {
1519				_compositeBlendObjwin(renderer, pixel, color | flags, current);
1520			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1521				_compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1522			} else if (renderer->blendEffect == BLEND_DARKEN) {
1523				_compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1524			}
1525		}
1526	}
1527}
1528
1529#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1530	SPRITE_YBASE_ ## DEPTH(inY); \
1531	unsigned tileData; \
1532	for (; outX < condition; ++outX, inX += xOffset) { \
1533		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1534			continue; \
1535		} \
1536		SPRITE_XBASE_ ## DEPTH(inX); \
1537		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1538	}
1539
1540#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1541	SPRITE_YBASE_ ## DEPTH(inY); \
1542	unsigned tileData; \
1543	if (outX % mosaicH) { \
1544		inX += (mosaicH - (outX % mosaicH)) * xOffset; \
1545		outX += mosaicH - (outX % mosaicH); \
1546	} \
1547	for (; outX < condition; ++outX, inX += xOffset) { \
1548		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1549			continue; \
1550		} \
1551		int localX = inX - xOffset * (outX % mosaicH); \
1552		SPRITE_XBASE_ ## DEPTH(localX); \
1553		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1554	}
1555
1556#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1557	unsigned tileData; \
1558	for (; outX < x + totalWidth && outX < end; ++outX, ++inX) { \
1559		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1560			continue; \
1561		} \
1562		xAccum += mat.a; \
1563		yAccum += mat.c; \
1564		int localX = (xAccum >> 8) + (width >> 1); \
1565		int localY = (yAccum >> 8) + (height >> 1); \
1566		\
1567		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1568			continue; \
1569		} \
1570		\
1571		SPRITE_YBASE_ ## DEPTH(localY); \
1572		SPRITE_XBASE_ ## DEPTH(localX); \
1573		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1574	}
1575
1576#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1577#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1578
1579#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1580	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1581	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1582	if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1583		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1584	}
1585
1586#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1587	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1588	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1589	if (tileData) { \
1590		renderer->row[outX] |= FLAG_OBJWIN; \
1591	}
1592
1593#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1594#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width : 0x80) + (localY & 0x7) * 8;
1595
1596#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1597	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1598	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1599	if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1600		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1601	}
1602
1603#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1604	LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1605	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1606	if (tileData) { \
1607		renderer->row[outX] |= FLAG_OBJWIN; \
1608	}
1609
1610static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1611	int width = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2];
1612	int height = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2 + 1];
1613	int start = renderer->start;
1614	int end = renderer->end;
1615	uint32_t flags = GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY;
1616	flags |= FLAG_TARGET_1 * ((GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
1617	flags |= FLAG_OBJWIN * (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN);
1618	int32_t x = GBAObjAttributesBGetX(sprite->b) << 23;
1619	x >>= 23;
1620	uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
1621	unsigned charBase = GBAObjAttributesCGetTile(sprite->c) * 0x20;
1622	int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1623	if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1624		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1625		variant = 0;
1626	}
1627	color_t* palette = &renderer->normalPalette[0x100];
1628	if (variant) {
1629		palette = &renderer->variantPalette[0x100];
1630	}
1631
1632	int inY = y - (int) GBAObjAttributesAGetY(sprite->a);
1633
1634	if (GBAObjAttributesAIsTransformed(sprite->a)) {
1635		int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a);
1636		int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a);
1637		struct GBAOAMMatrix mat;
1638		LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
1639		LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
1640		LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
1641		LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
1642
1643		if (inY < 0) {
1644			inY += 256;
1645		}
1646		int outX = x >= start ? x : start;
1647		int inX = outX - x;
1648		int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1));
1649		int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1));
1650
1651		if (!GBAObjAttributesAIs256Color(sprite->a)) {
1652			palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1653			if (flags & FLAG_OBJWIN) {
1654				SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1655			} else {
1656				SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1657			}
1658		} else {
1659			if (flags & FLAG_OBJWIN) {
1660				SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1661			} else {
1662				SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1663			}
1664		}
1665	} else {
1666		int outX = x >= start ? x : start;
1667		int condition = x + width;
1668		int mosaicH = 1;
1669		if (GBAObjAttributesAIsMosaic(sprite->a)) {
1670			mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
1671			if (condition % mosaicH) {
1672				condition += mosaicH - (condition % mosaicH);
1673			}
1674		}
1675		if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) {
1676			inY += 256;
1677		}
1678		if (GBAObjAttributesBIsVFlip(sprite->b)) {
1679			inY = height - inY - 1;
1680		}
1681		if (end < condition) {
1682			condition = end;
1683		}
1684		int inX = outX - x;
1685		int xOffset = 1;
1686		if (GBAObjAttributesBIsHFlip(sprite->b)) {
1687			inX = width - inX - 1;
1688			xOffset = -1;
1689		}
1690		if (!GBAObjAttributesAIs256Color(sprite->a)) {
1691			palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1692			if (flags & FLAG_OBJWIN) {
1693				SPRITE_NORMAL_LOOP(16, OBJWIN);
1694			} else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1695				SPRITE_MOSAIC_LOOP(16, NORMAL);
1696			} else {
1697				SPRITE_NORMAL_LOOP(16, NORMAL);
1698			}
1699		} else {
1700			if (flags & FLAG_OBJWIN) {
1701				SPRITE_NORMAL_LOOP(256, OBJWIN);
1702			} else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1703				SPRITE_MOSAIC_LOOP(256, NORMAL);
1704			} else {
1705				SPRITE_NORMAL_LOOP(256, NORMAL);
1706			}
1707		}
1708	}
1709	return 1;
1710}
1711
1712static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1713	int x;
1714	uint32_t* pixel = &renderer->row[renderer->start];
1715	uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
1716
1717	int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt);
1718	bool objwinDisable = false;
1719	bool objwinOnly = false;
1720	if (objwinSlowPath) {
1721		objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed);
1722		objwinOnly = !objwinDisable && !GBAWindowControlIsObjEnable(renderer->currentWindow.packed);
1723
1724		if (objwinDisable) {
1725			for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1726				uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1727				uint32_t current = *pixel;
1728				if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1729					_compositeBlendObjwin(renderer, pixel, color | flags, current);
1730				}
1731			}
1732			return;
1733		} else if (objwinOnly) {
1734			for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1735				uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1736				uint32_t current = *pixel;
1737				if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1738					_compositeBlendObjwin(renderer, pixel, color | flags, current);
1739				}
1740			}
1741			return;
1742		} else {
1743			for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1744				uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1745				uint32_t current = *pixel;
1746				if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1747					_compositeBlendObjwin(renderer, pixel, color | flags, current);
1748				}
1749			}
1750			return;
1751		}
1752	}
1753	for (x = renderer->start; x < renderer->end; ++x, ++pixel) {
1754		uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1755		uint32_t current = *pixel;
1756		if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1757			_compositeBlendNoObjwin(renderer, pixel, color | flags, current);
1758		}
1759	}
1760}
1761
1762static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1763	int i;
1764	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1765		for (i = 0; i < 512; ++i) {
1766			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1767		}
1768	} else if (renderer->blendEffect == BLEND_DARKEN) {
1769		for (i = 0; i < 512; ++i) {
1770			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1771		}
1772	} else {
1773		for (i = 0; i < 512; ++i) {
1774			renderer->variantPalette[i] = renderer->normalPalette[i];
1775		}
1776	}
1777}
1778
1779static inline unsigned _brighten(unsigned color, int y) {
1780	unsigned c = 0;
1781	unsigned a;
1782#ifdef COLOR_16_BIT
1783	a = color & 0x1F;
1784	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1785
1786#ifdef COLOR_5_6_5
1787	a = color & 0x7C0;
1788	c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1789
1790	a = color & 0xF800;
1791	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1792#else
1793	a = color & 0x3E0;
1794	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1795
1796	a = color & 0x7C00;
1797	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1798#endif
1799#else
1800	a = color & 0xF8;
1801	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1802
1803	a = color & 0xF800;
1804	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1805
1806	a = color & 0xF80000;
1807	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1808#endif
1809	return c;
1810}
1811
1812static inline unsigned _darken(unsigned color, int y) {
1813	unsigned c = 0;
1814	unsigned a;
1815#ifdef COLOR_16_BIT
1816	a = color & 0x1F;
1817	c |= (a - (a * y) / 16) & 0x1F;
1818
1819#ifdef COLOR_5_6_5
1820	a = color & 0x7C0;
1821	c |= (a - (a * y) / 16) & 0x7C0;
1822
1823	a = color & 0xF800;
1824	c |= (a - (a * y) / 16) & 0xF800;
1825#else
1826	a = color & 0x3E0;
1827	c |= (a - (a * y) / 16) & 0x3E0;
1828
1829	a = color & 0x7C00;
1830	c |= (a - (a * y) / 16) & 0x7C00;
1831#endif
1832#else
1833	a = color & 0xF8;
1834	c |= (a - (a * y) / 16) & 0xF8;
1835
1836	a = color & 0xF800;
1837	c |= (a - (a * y) / 16) & 0xF800;
1838
1839	a = color & 0xF80000;
1840	c |= (a - (a * y) / 16) & 0xF80000;
1841#endif
1842	return c;
1843}
1844
1845static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1846	unsigned c = 0;
1847	unsigned a, b;
1848#ifdef COLOR_16_BIT
1849#ifdef COLOR_5_6_5
1850	a = colorA & 0xF81F;
1851	b = colorB & 0xF81F;
1852	a |= (colorA & 0x7C0) << 16;
1853	b |= (colorB & 0x7C0) << 16;
1854	c = ((a * weightA + b * weightB) / 16);
1855	if (c & 0x08000000) {
1856		c = (c & ~0x0FC00000) | 0x07C00000;
1857	}
1858	if (c & 0x0020) {
1859		c = (c & ~0x003F) | 0x001F;
1860	}
1861	if (c & 0x10000) {
1862		c = (c & ~0x1F800) | 0xF800;
1863	}
1864	c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
1865#else
1866	a = colorA & 0x7C1F;
1867	b = colorB & 0x7C1F;
1868	a |= (colorA & 0x3E0) << 16;
1869	b |= (colorB & 0x3E0) << 16;
1870	c = ((a * weightA + b * weightB) / 16);
1871	if (c & 0x04000000) {
1872		c = (c & ~0x07E00000) | 0x03E00000;
1873	}
1874	if (c & 0x0020) {
1875		c = (c & ~0x003F) | 0x001F;
1876	}
1877	if (c & 0x10000) {
1878		c = (c & ~0x1F800) | 0xF800;
1879	}
1880	c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
1881#endif
1882#else
1883	a = colorA & 0xF8;
1884	b = colorB & 0xF8;
1885	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1886	if (c & 0x00000100) {
1887		c = 0x000000F8;
1888	}
1889
1890	a = colorA & 0xF800;
1891	b = colorB & 0xF800;
1892	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1893	if (c & 0x00010000) {
1894		c = (c & 0x000000F8) | 0x0000F800;
1895	}
1896
1897	a = colorA & 0xF80000;
1898	b = colorB & 0xF80000;
1899	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1900	if (c & 0x01000000) {
1901		c = (c & 0x0000F8F8) | 0x00F80000;
1902	}
1903#endif
1904	return c;
1905}