all repos — mgba @ f08e466fce8798d52687ea390882027e1ee0d880

mGBA Game Boy Advance Emulator

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

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