all repos — mgba @ 4f8c021f9560ad7660599ae80b712a236128ed43

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