all repos — mgba @ 8a6254cc73053ee75d60e99d01daa7c8c4e5d6a9

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