all repos — mgba @ 8bbac2f512519b5502748e67e4dae8c36b71547c

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