all repos — mgba @ 620adbd5777f75fbb5d7b01b1ac534d72293f240

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	memset(softwareRenderer->spriteLayer, 0, sizeof(softwareRenderer->spriteLayer));
 359
 360	softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
 361	softwareRenderer->nWindows = 1;
 362	if (softwareRenderer->dispcnt.win0Enable || softwareRenderer->dispcnt.win1Enable || softwareRenderer->dispcnt.objwinEnable) {
 363		softwareRenderer->windows[0].control = softwareRenderer->winout;
 364		if (softwareRenderer->dispcnt.win1Enable && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) {
 365			_breakWindow(softwareRenderer, &softwareRenderer->winN[1]);
 366		}
 367		if (softwareRenderer->dispcnt.win0Enable && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) {
 368			_breakWindow(softwareRenderer, &softwareRenderer->winN[0]);
 369		}
 370	} else {
 371		softwareRenderer->windows[0].control.packed = 0xFF;
 372	}
 373
 374	int w;
 375	int x = 0;
 376	for (w = 0; w < softwareRenderer->nWindows; ++w) {
 377		// TOOD: handle objwin on backdrop
 378		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
 379		if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
 380			backdrop |= softwareRenderer->normalPalette[0];
 381		} else {
 382			backdrop |= softwareRenderer->variantPalette[0];
 383		}
 384		int end = softwareRenderer->windows[w].endX;
 385		for (; x < end; ++x) {
 386			softwareRenderer->row[x] = backdrop;
 387		}
 388	}
 389
 390	_drawScanline(softwareRenderer, y);
 391
 392	if (softwareRenderer->target2Bd) {
 393		x = 0;
 394		for (w = 0; w < softwareRenderer->nWindows; ++w) {
 395		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
 396			if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
 397				backdrop |= softwareRenderer->normalPalette[0];
 398			} else {
 399				backdrop |= softwareRenderer->variantPalette[0];
 400			}
 401			int end = softwareRenderer->windows[w].endX;
 402			for (; x < end; ++x) {
 403				uint32_t color = softwareRenderer->row[x];
 404				if (color & FLAG_TARGET_1 && !(color & FLAG_FINALIZED)) {
 405					softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
 406				}
 407			}
 408		}
 409	}
 410
 411#ifdef COLOR_16_BIT
 412#ifdef __arm__
 413	_to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
 414#else
 415	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
 416		row[x] = softwareRenderer->row[x];
 417	}
 418#endif
 419#else
 420	memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
 421#endif
 422}
 423
 424static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
 425	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 426
 427	softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
 428	softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
 429	softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
 430	softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
 431}
 432
 433static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
 434	renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
 435	renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
 436	renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
 437	renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
 438}
 439
 440static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 441	(void)(renderer);
 442	union GBARegisterBGCNT reg = { .packed = value };
 443	bg->priority = reg.priority;
 444	bg->charBase = reg.charBase << 14;
 445	bg->mosaic = reg.mosaic;
 446	bg->multipalette = reg.multipalette;
 447	bg->screenBase = reg.screenBase << 11;
 448	bg->overflow = reg.overflow;
 449	bg->size = reg.size;
 450}
 451
 452static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 453	bg->dx = value;
 454}
 455
 456static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 457	bg->dmx = value;
 458}
 459
 460static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 461	bg->dy = value;
 462}
 463
 464static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 465	bg->dmy = value;
 466}
 467
 468static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 469	bg->refx = (bg->refx & 0xFFFF0000) | value;
 470	bg->sx = bg->refx;
 471}
 472
 473static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 474	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
 475	bg->refx <<= 4;
 476	bg->refx >>= 4;
 477	bg->sx = bg->refx;
 478}
 479
 480static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 481	bg->refy = (bg->refy & 0xFFFF0000) | value;
 482	bg->sy = bg->refy;
 483}
 484
 485static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
 486	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
 487	bg->refy <<= 4;
 488	bg->refy >>= 4;
 489	bg->sy = bg->refy;
 490}
 491
 492static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
 493	union {
 494		struct {
 495			unsigned target1Bg0 : 1;
 496			unsigned target1Bg1 : 1;
 497			unsigned target1Bg2 : 1;
 498			unsigned target1Bg3 : 1;
 499			unsigned target1Obj : 1;
 500			unsigned target1Bd : 1;
 501			enum BlendEffect effect : 2;
 502			unsigned target2Bg0 : 1;
 503			unsigned target2Bg1 : 1;
 504			unsigned target2Bg2 : 1;
 505			unsigned target2Bg3 : 1;
 506			unsigned target2Obj : 1;
 507			unsigned target2Bd : 1;
 508		};
 509		uint16_t packed;
 510	} bldcnt = { .packed = value };
 511
 512	enum BlendEffect oldEffect = renderer->blendEffect;
 513
 514	renderer->bg[0].target1 = bldcnt.target1Bg0;
 515	renderer->bg[1].target1 = bldcnt.target1Bg1;
 516	renderer->bg[2].target1 = bldcnt.target1Bg2;
 517	renderer->bg[3].target1 = bldcnt.target1Bg3;
 518	renderer->bg[0].target2 = bldcnt.target2Bg0;
 519	renderer->bg[1].target2 = bldcnt.target2Bg1;
 520	renderer->bg[2].target2 = bldcnt.target2Bg2;
 521	renderer->bg[3].target2 = bldcnt.target2Bg3;
 522
 523	renderer->blendEffect = bldcnt.effect;
 524	renderer->target1Obj = bldcnt.target1Obj;
 525	renderer->target1Bd = bldcnt.target1Bd;
 526	renderer->target2Obj = bldcnt.target2Obj;
 527	renderer->target2Bd = bldcnt.target2Bd;
 528
 529	renderer->anyTarget2 = bldcnt.packed & 0x3F00;
 530
 531	if (oldEffect != renderer->blendEffect) {
 532		_updatePalettes(renderer);
 533	}
 534}
 535
 536#define TEST_LAYER_ENABLED(X) \
 537	(renderer->bg[X].enabled && \
 538	(renderer->currentWindow.bg ## X ## Enable || \
 539	(renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
 540	renderer->bg[X].priority == priority)
 541
 542static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
 543	int w;
 544	renderer->end = 0;
 545	int spriteLayers = 0;
 546	if (renderer->dispcnt.objEnable) {
 547		for (w = 0; w < renderer->nWindows; ++w) {
 548			renderer->start = renderer->end;
 549			renderer->end = renderer->windows[w].endX;
 550			renderer->currentWindow = renderer->windows[w].control;
 551			if (!renderer->currentWindow.objEnable) {
 552				continue;
 553			}
 554			int i;
 555			int drawn;
 556			for (i = 0; i < 128; ++i) {
 557				struct GBAObj* sprite = &renderer->d.oam->obj[i];
 558				if (sprite->transformed) {
 559					drawn = _preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
 560					spriteLayers |= drawn << sprite->priority;
 561				} else if (!sprite->disable) {
 562					drawn = _preprocessSprite(renderer, sprite, y);
 563					spriteLayers |= drawn << sprite->priority;
 564				}
 565			}
 566		}
 567	}
 568
 569	int priority;
 570	for (priority = 0; priority < 4; ++priority) {
 571		if (spriteLayers & (1 << priority)) {
 572			_postprocessSprite(renderer, priority);
 573		}
 574		renderer->end = 0;
 575		for (w = 0; w < renderer->nWindows; ++w) {
 576			renderer->start = renderer->end;
 577			renderer->end = renderer->windows[w].endX;
 578			renderer->currentWindow = renderer->windows[w].control;
 579			if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
 580				_drawBackgroundMode0(renderer, &renderer->bg[0], y);
 581			}
 582			if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
 583				_drawBackgroundMode0(renderer, &renderer->bg[1], y);
 584			}
 585			if (TEST_LAYER_ENABLED(2)) {
 586				switch (renderer->dispcnt.mode) {
 587				case 0:
 588					_drawBackgroundMode0(renderer, &renderer->bg[2], y);
 589					break;
 590				case 1:
 591				case 2:
 592					_drawBackgroundMode2(renderer, &renderer->bg[2], y);
 593					break;
 594				case 3:
 595					_drawBackgroundMode3(renderer, &renderer->bg[2], y);
 596					break;
 597				case 4:
 598					_drawBackgroundMode4(renderer, &renderer->bg[2], y);
 599					break;
 600				case 5:
 601					_drawBackgroundMode5(renderer, &renderer->bg[2], y);
 602					break;
 603				}
 604			}
 605			if (TEST_LAYER_ENABLED(3)) {
 606				switch (renderer->dispcnt.mode) {
 607				case 0:
 608					_drawBackgroundMode0(renderer, &renderer->bg[3], y);
 609					break;
 610				case 2:
 611					_drawBackgroundMode2(renderer, &renderer->bg[3], y);
 612					break;
 613				}
 614			}
 615		}
 616	}
 617	renderer->bg[2].sx += renderer->bg[2].dmx;
 618	renderer->bg[2].sy += renderer->bg[2].dmy;
 619	renderer->bg[3].sx += renderer->bg[3].dmx;
 620	renderer->bg[3].sy += renderer->bg[3].dmy;
 621}
 622
 623static void _composite(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
 624	// We stash the priority on the top bits so we can do a one-operator comparison
 625	// The lower the number, the higher the priority, and sprites take precendence over backgrounds
 626	// We want to do special processing if the color pixel is target 1, however
 627	if (current & FLAG_UNWRITTEN) {
 628		color |= (current & FLAG_OBJWIN);
 629	} else if ((color & FLAG_ORDER_MASK) < (current & FLAG_ORDER_MASK)) {
 630		if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
 631			color |= FLAG_FINALIZED;
 632		} else {
 633			color = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
 634		}
 635	} else {
 636		if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
 637			color = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
 638		} else {
 639			color = current | FLAG_FINALIZED;
 640		}
 641	}
 642	*pixel = color;
 643}
 644
 645#define BACKGROUND_DRAW_PIXEL_16 \
 646	pixelData = tileData & 0xF; \
 647	current = *pixel; \
 648	if (pixelData && !(current & FLAG_FINALIZED)) { \
 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 && !(current & FLAG_FINALIZED)) { \
 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) | FLAG_IS_BACKGROUND;
 738	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
 739	flags |= FLAG_TARGET_2 * background->target2;
 740	if (!renderer->anyTarget2) {
 741		flags |= FLAG_FINALIZED;
 742	}
 743
 744	uint32_t screenBase;
 745	uint32_t charBase;
 746	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
 747	color_t* mainPalette = renderer->normalPalette;
 748	if (variant) {
 749		mainPalette = renderer->variantPalette;
 750	}
 751	color_t* palette = mainPalette;
 752	PREPARE_OBJWIN;
 753
 754	int outX = renderer->start;
 755
 756	uint32_t tileData;
 757	uint32_t current;
 758	int pixelData;
 759	int paletteData;
 760	int tileX = 0;
 761	int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
 762
 763	if (inX & 0x7) {
 764		int mod8 = inX & 0x7;
 765		BACKGROUND_TEXT_SELECT_CHARACTER;
 766
 767		int end = outX + 0x8 - mod8;
 768		if (end > renderer->end) {
 769			// TODO: ensure tiles are properly aligned from this
 770			end = renderer->end;
 771		}
 772		if (!background->multipalette) {
 773			paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
 774			palette = &mainPalette[paletteData];
 775			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
 776			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 777			if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 778				tileData >>= 4 * mod8;
 779				for (; outX < end; ++outX) {
 780					uint32_t* pixel = &renderer->row[outX];
 781					BACKGROUND_DRAW_PIXEL_16;
 782				}
 783			} else {
 784				for (outX = end - 1; outX >= renderer->start; --outX) {
 785					uint32_t* pixel = &renderer->row[outX];
 786					BACKGROUND_DRAW_PIXEL_16;
 787				}
 788			}
 789		} else {
 790			// TODO: hflip
 791			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
 792			int end2 = end - 4;
 793			int shift = inX & 0x3;
 794			if (end2 > 0) {
 795				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 796				tileData >>= 8 * shift;
 797				shift = 0;
 798				for (; outX < end2; ++outX) {
 799					uint32_t* pixel = &renderer->row[outX];
 800					BACKGROUND_DRAW_PIXEL_256;
 801				}
 802			}
 803
 804			tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 805			tileData >>= 8 * shift;
 806			for (; outX < end; ++outX) {
 807				uint32_t* pixel = &renderer->row[outX];
 808				BACKGROUND_DRAW_PIXEL_256;
 809			}
 810		}
 811	}
 812	if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) {
 813		tileX = tileEnd;
 814		int pixelData, paletteData;
 815		int mod8 = (inX + renderer->end - renderer->start) & 0x7;
 816		BACKGROUND_TEXT_SELECT_CHARACTER;
 817
 818		int end = 0x8 - mod8;
 819		if (!background->multipalette) {
 820			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
 821			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 822			paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
 823			palette = &mainPalette[paletteData];
 824			if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 825				outX = renderer->end - mod8;
 826				if (outX < renderer->start) {
 827					tileData >>= 4 * (renderer->start - outX);
 828					outX = renderer->start;
 829				}
 830				for (; outX < renderer->end; ++outX) {
 831					uint32_t* pixel = &renderer->row[outX];
 832					BACKGROUND_DRAW_PIXEL_16;
 833				}
 834			} else {
 835				tileData >>= 4 * (0x8 - mod8);
 836				int end2 = renderer->end - 8;
 837				if (end2 < -1) {
 838					end2 = -1;
 839				}
 840				for (outX = renderer->end - 1; outX > end2; --outX) {
 841					uint32_t* pixel = &renderer->row[outX];
 842					BACKGROUND_DRAW_PIXEL_16;
 843				}
 844			}
 845		} else {
 846			// TODO: hflip
 847			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
 848			outX = renderer->end - 8 + end;
 849			int end2 = 4 - end;
 850			if (end2 > 0) {
 851				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 852				for (; outX < renderer->end - end2; ++outX) {
 853					uint32_t* pixel = &renderer->row[outX];
 854					BACKGROUND_DRAW_PIXEL_256;
 855				}
 856				++charBase;
 857			}
 858
 859			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 860			for (; outX < renderer->end; ++outX) {
 861				uint32_t* pixel = &renderer->row[outX];
 862				BACKGROUND_DRAW_PIXEL_256;
 863			}
 864		}
 865
 866		tileX = (inX & 0x7) != 0;
 867		outX = renderer->start + tileX * 8 - (inX & 0x7);
 868	}
 869
 870	uint32_t* pixel = &renderer->row[outX];
 871	if (background->mosaic) {
 872		int mosaicH = renderer->mosaic.bgH + 1;
 873		int x;
 874		int mosaicWait = outX % mosaicH;
 875		int carryData = 0;
 876
 877		if (!background->multipalette) {
 878			for (; tileX < tileEnd; ++tileX) {
 879				BACKGROUND_TEXT_SELECT_CHARACTER;
 880				charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
 881				tileData = carryData;
 882				for (x = 0; x < 8; ++x) {
 883					if (!mosaicWait) {
 884						paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
 885						palette = &mainPalette[paletteData];
 886						tileData = ((uint32_t*)renderer->d.vram)[charBase];
 887						if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 888							tileData >>= x * 4;
 889						} else {
 890							tileData >>= (7 - x) * 4;
 891						}
 892						tileData &= 0xF;
 893						tileData |= tileData << 4;
 894						tileData |= tileData << 8;
 895						tileData |= tileData << 12;
 896						tileData |= tileData << 16;
 897						tileData |= tileData << 20;
 898						tileData |= tileData << 24;
 899						tileData |= tileData << 28;
 900						carryData = tileData;
 901						mosaicWait = mosaicH;
 902					}
 903					--mosaicWait;
 904					BACKGROUND_DRAW_PIXEL_16;
 905					++pixel;
 906				}
 907			}
 908		} else {
 909			for (; tileX < tileEnd; ++tileX) {
 910				BACKGROUND_TEXT_SELECT_CHARACTER;
 911				charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
 912				tileData = carryData;
 913				for (x = 0; x < 8; ++x) {
 914					if (!mosaicWait) {
 915						if (x >= 4) {
 916							tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 917							if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 918								tileData >>= (x - 4) * 8;
 919							} else {
 920								tileData >>= (7 - x) * 8;
 921							}
 922						} else {
 923							tileData = ((uint32_t*)renderer->d.vram)[charBase];
 924							if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 925								tileData >>= x * 8;
 926							} else {
 927								tileData >>= (3 - x) * 8;
 928							}
 929						}
 930						tileData &= 0xFF;
 931						carryData = tileData;
 932						mosaicWait = mosaicH;
 933					}
 934					tileData |= tileData << 8;
 935					--mosaicWait;
 936					BACKGROUND_DRAW_PIXEL_256;
 937					++pixel;
 938				}
 939			}
 940		}
 941		return;
 942	}
 943
 944	if (!background->multipalette) {
 945		for (; tileX < tileEnd; ++tileX) {
 946			BACKGROUND_TEXT_SELECT_CHARACTER;
 947			paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
 948			palette = &mainPalette[paletteData];
 949			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
 950			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 951			if (tileData) {
 952				if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 953					BACKGROUND_DRAW_PIXEL_16;
 954					++pixel;
 955					BACKGROUND_DRAW_PIXEL_16;
 956					++pixel;
 957					BACKGROUND_DRAW_PIXEL_16;
 958					++pixel;
 959					BACKGROUND_DRAW_PIXEL_16;
 960					++pixel;
 961					BACKGROUND_DRAW_PIXEL_16;
 962					++pixel;
 963					BACKGROUND_DRAW_PIXEL_16;
 964					++pixel;
 965					BACKGROUND_DRAW_PIXEL_16;
 966					++pixel;
 967					BACKGROUND_DRAW_PIXEL_16;
 968					++pixel;
 969				} else {
 970					pixel += 7;
 971					BACKGROUND_DRAW_PIXEL_16;
 972					--pixel;
 973					BACKGROUND_DRAW_PIXEL_16;
 974					--pixel;
 975					BACKGROUND_DRAW_PIXEL_16;
 976					--pixel;
 977					BACKGROUND_DRAW_PIXEL_16;
 978					--pixel;
 979					BACKGROUND_DRAW_PIXEL_16;
 980					--pixel;
 981					BACKGROUND_DRAW_PIXEL_16;
 982					--pixel;
 983					BACKGROUND_DRAW_PIXEL_16;
 984					--pixel;
 985					BACKGROUND_DRAW_PIXEL_16;
 986					pixel += 8;
 987				}
 988			} else {
 989				pixel += 8;
 990			}
 991		}
 992	} else {
 993		for (; tileX < tileEnd; ++tileX) {
 994			BACKGROUND_TEXT_SELECT_CHARACTER;
 995			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
 996			if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 997				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 998				if (tileData) {
 999						BACKGROUND_DRAW_PIXEL_256;
1000						++pixel;
1001						BACKGROUND_DRAW_PIXEL_256;
1002						++pixel;
1003						BACKGROUND_DRAW_PIXEL_256;
1004						++pixel;
1005						BACKGROUND_DRAW_PIXEL_256;
1006						++pixel;
1007				} else {
1008					pixel += 4;
1009				}
1010				tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
1011				if (tileData) {
1012						BACKGROUND_DRAW_PIXEL_256;
1013						++pixel;
1014						BACKGROUND_DRAW_PIXEL_256;
1015						++pixel;
1016						BACKGROUND_DRAW_PIXEL_256;
1017						++pixel;
1018						BACKGROUND_DRAW_PIXEL_256;
1019						++pixel;
1020				} else {
1021					pixel += 4;
1022				}
1023			} else {
1024				uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
1025				if (tileData) {
1026					pixel += 3;
1027					BACKGROUND_DRAW_PIXEL_256;
1028					--pixel;
1029					BACKGROUND_DRAW_PIXEL_256;
1030					--pixel;
1031					BACKGROUND_DRAW_PIXEL_256;
1032					--pixel;
1033					BACKGROUND_DRAW_PIXEL_256;
1034				}
1035				pixel += 4;
1036				tileData = ((uint32_t*)renderer->d.vram)[charBase];
1037				if (tileData) {
1038					pixel += 3;
1039					BACKGROUND_DRAW_PIXEL_256;
1040					--pixel;
1041					BACKGROUND_DRAW_PIXEL_256;
1042					--pixel;
1043					BACKGROUND_DRAW_PIXEL_256;
1044					--pixel;
1045					BACKGROUND_DRAW_PIXEL_256;
1046				}
1047				pixel += 4;
1048			}
1049		}
1050	}
1051}
1052
1053#define BACKGROUND_BITMAP_INIT \
1054	(void)(unused); \
1055	int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1056	int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1057	int32_t localX; \
1058	int32_t localY; \
1059	\
1060	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
1061	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1062	flags |= FLAG_TARGET_2 * background->target2; \
1063	if (!renderer->anyTarget2) { \
1064		flags |= FLAG_FINALIZED; \
1065	} \
1066	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1067	color_t* palette = renderer->normalPalette; \
1068	if (variant) { \
1069		palette = renderer->variantPalette; \
1070	} \
1071	PREPARE_OBJWIN;
1072
1073#define BACKGROUND_BITMAP_ITERATE(W, H) \
1074	x += background->dx; \
1075	y += background->dy; \
1076	\
1077	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1078		continue; \
1079	} else { \
1080		localX = x; \
1081		localY = y; \
1082	}
1083
1084static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1085	int sizeAdjusted = 0x8000 << background->size;
1086
1087	BACKGROUND_BITMAP_INIT;
1088
1089	uint32_t screenBase = background->screenBase;
1090	uint32_t charBase = background->charBase;
1091	uint8_t mapData;
1092	uint8_t tileData;
1093
1094	int outX;
1095	uint32_t* pixel;
1096	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1097		x += background->dx;
1098		y += background->dy;
1099
1100		if (background->overflow) {
1101			localX = x & (sizeAdjusted - 1);
1102			localY = y & (sizeAdjusted - 1);
1103		} else if ((x | y) & ~(sizeAdjusted - 1)) {
1104			continue;
1105		} else {
1106			localX = x;
1107			localY = y;
1108		}
1109		mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1110		tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1111
1112		uint32_t current = *pixel;
1113		if (tileData && !(current & FLAG_FINALIZED)) {
1114			if (!objwinSlowPath) {
1115				_composite(renderer, pixel, palette[tileData] | flags, current);
1116			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1117				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1118				_composite(renderer, pixel, currentPalette[tileData] | flags, current);
1119			}
1120		}
1121	}
1122}
1123
1124static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1125	BACKGROUND_BITMAP_INIT;
1126
1127	uint32_t color;
1128
1129	int outX;
1130	uint32_t* pixel;
1131	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1132		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1133
1134		color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1135#ifndef COLOR_16_BIT
1136		unsigned color32;
1137		color32 = 0;
1138		color32 |= (color << 3) & 0xF8;
1139		color32 |= (color << 6) & 0xF800;
1140		color32 |= (color << 9) & 0xF80000;
1141		color = color32;
1142#endif
1143
1144		uint32_t current = *pixel;
1145		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1146			if (!variant) {
1147				_composite(renderer, pixel, color | flags, current);
1148			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1149				_composite(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1150			} else if (renderer->blendEffect == BLEND_DARKEN) {
1151				_composite(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1152			}
1153		}
1154	}
1155}
1156
1157static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1158	BACKGROUND_BITMAP_INIT;
1159
1160	uint16_t color;
1161	uint32_t offset = 0;
1162	if (renderer->dispcnt.frameSelect) {
1163		offset = 0xA000;
1164	}
1165
1166	int outX;
1167	uint32_t* pixel;
1168	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1169		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1170
1171		color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1172
1173		uint32_t current = *pixel;
1174		if (color && !(current & FLAG_FINALIZED)) {
1175			if (!objwinSlowPath) {
1176				_composite(renderer, pixel, palette[color] | flags, current);
1177			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1178				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1179				_composite(renderer, pixel, currentPalette[color] | flags, current);
1180			}
1181		}
1182	}
1183}
1184
1185static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1186	BACKGROUND_BITMAP_INIT;
1187
1188	uint32_t color;
1189	uint32_t offset = 0;
1190	if (renderer->dispcnt.frameSelect) {
1191		offset = 0xA000;
1192	}
1193
1194	int outX;
1195	uint32_t* pixel;
1196	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1197		BACKGROUND_BITMAP_ITERATE(160, 128);
1198
1199		color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1200#ifndef COLOR_16_BIT
1201		unsigned color32 = 0;
1202		color32 |= (color << 9) & 0xF80000;
1203		color32 |= (color << 3) & 0xF8;
1204		color32 |= (color << 6) & 0xF800;
1205		color = color32;
1206#endif
1207
1208		uint32_t current = *pixel;
1209		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1210			if (!variant) {
1211				_composite(renderer, pixel, color | flags, current);
1212			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1213				_composite(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1214			} else if (renderer->blendEffect == BLEND_DARKEN) {
1215				_composite(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1216			}
1217		}
1218	}
1219}
1220
1221static const int _objSizes[32] = {
1222	8, 8,
1223	16, 16,
1224	32, 32,
1225	64, 64,
1226	16, 8,
1227	32, 8,
1228	32, 16,
1229	64, 32,
1230	8, 16,
1231	8, 32,
1232	16, 32,
1233	32, 64,
1234	0, 0,
1235	0, 0,
1236	0, 0,
1237	0, 0
1238};
1239
1240#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1241	SPRITE_YBASE_ ## DEPTH(inY); \
1242	for (; outX < condition; ++outX, inX += xOffset) { \
1243		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1244			continue; \
1245		} \
1246		SPRITE_XBASE_ ## DEPTH(inX); \
1247		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1248	}
1249
1250#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1251	SPRITE_YBASE_ ## DEPTH(inY); \
1252	if (outX % mosaicH) { \
1253		inX += (mosaicH - (outX % mosaicH)) * xOffset; \
1254		outX += mosaicH - (outX % mosaicH); \
1255	} \
1256	for (; outX < condition; ++outX, inX += xOffset) { \
1257		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1258			continue; \
1259		} \
1260		int localX = inX - xOffset * (outX % mosaicH); \
1261		SPRITE_XBASE_ ## DEPTH(localX); \
1262		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1263	}
1264
1265#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1266	int outX; \
1267	for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1268		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1269			continue; \
1270		} \
1271		int inX = outX - x; \
1272		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1273		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1274		\
1275		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1276			continue; \
1277		} \
1278		\
1279		SPRITE_YBASE_ ## DEPTH(localY); \
1280		SPRITE_XBASE_ ## DEPTH(localX); \
1281		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1282	}
1283
1284#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1285#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1286
1287#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1288	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1289	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1290	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1291		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1292	}
1293
1294#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1295	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1296	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1297	if (tileData) { \
1298		renderer->row[outX] |= FLAG_OBJWIN; \
1299	}
1300
1301#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1302#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1303
1304#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1305	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1306	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1307	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1308		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1309	}
1310
1311#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1312	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1313	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1314	if (tileData) { \
1315		renderer->row[outX] |= FLAG_OBJWIN; \
1316	}
1317
1318static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1319	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1320	if (sprite->mosaic) {
1321		int mosaicV = renderer->mosaic.objV + 1;
1322		y -= y % mosaicV;
1323	}
1324	if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1325		return 0;
1326	}
1327	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1328	int start = renderer->start;
1329	int end = renderer->end;
1330	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1331	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1332	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1333	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1334	int x = sprite->x;
1335	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1336	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1337	if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1338		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1339		variant = 0;
1340	}
1341	color_t* palette = &renderer->normalPalette[0x100];
1342	if (variant) {
1343		palette = &renderer->variantPalette[0x100];
1344	}
1345
1346	int outX = x >= start ? x : start;
1347	int condition = x + width;
1348	int mosaicH = 1;
1349	if (sprite->mosaic) {
1350		mosaicH = renderer->mosaic.objH + 1;
1351		if (condition % mosaicH) {
1352			condition += mosaicH - (condition % mosaicH);
1353		}
1354	}
1355	int inY = y - sprite->y;
1356	if (sprite->y + height - 256 >= 0) {
1357		inY += 256;
1358	}
1359	if (sprite->vflip) {
1360		inY = height - inY - 1;
1361	}
1362	if (end < condition) {
1363		condition = end;
1364	}
1365	int inX = outX - x;
1366	int xOffset = 1;
1367	if (sprite->hflip) {
1368		inX = width - inX - 1;
1369		xOffset = -1;
1370	}
1371	if (!sprite->multipalette) {
1372		palette = &palette[sprite->palette << 4];
1373		if (flags & FLAG_OBJWIN) {
1374			SPRITE_NORMAL_LOOP(16, OBJWIN);
1375		} else if (sprite->mosaic) {
1376			SPRITE_MOSAIC_LOOP(16, NORMAL);
1377		} else {
1378			SPRITE_NORMAL_LOOP(16, NORMAL);
1379		}
1380	} else {
1381		if (flags & FLAG_OBJWIN) {
1382			SPRITE_NORMAL_LOOP(256, OBJWIN);
1383		} else if (sprite->mosaic) {
1384			SPRITE_MOSAIC_LOOP(256, NORMAL);
1385		} else {
1386			SPRITE_NORMAL_LOOP(256, NORMAL);
1387		}
1388	}
1389	return 1;
1390}
1391
1392static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1393	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1394	int totalHeight = height << sprite->doublesize;
1395	if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1396		return 0;
1397	}
1398	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1399	int totalWidth = width << sprite->doublesize;
1400	int start = renderer->start;
1401	int end = renderer->end;
1402	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1403	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1404	flags |= FLAG_TARGET_2 * renderer->target2Obj;
1405	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1406	int x = sprite->x;
1407	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1408	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1409	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1410	if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1411		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1412		variant = 0;
1413	}
1414	color_t* palette = &renderer->normalPalette[0x100];
1415	if (variant) {
1416		palette = &renderer->variantPalette[0x100];
1417	}
1418	int inY = y - sprite->y;
1419	if (inY < 0) {
1420		inY += 256;
1421	}
1422	if (!sprite->multipalette) {
1423		palette = &palette[sprite->palette << 4];
1424		if (flags & FLAG_OBJWIN) {
1425			SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1426		} else {
1427			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1428		}
1429	} else {
1430		if (flags & FLAG_OBJWIN) {
1431			SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1432		} else {
1433			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1434		}
1435	}
1436	return 1;
1437}
1438
1439static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1440	int x;
1441	uint32_t* pixel = renderer->row;
1442	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1443		uint32_t color = renderer->spriteLayer[x];
1444		uint32_t current = *pixel;
1445		if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1446			_composite(renderer, pixel, color & ~FLAG_FINALIZED, current);
1447		}
1448	}
1449}
1450
1451static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1452	int i;
1453	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1454		for (i = 0; i < 512; ++i) {
1455			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1456		}
1457	} else if (renderer->blendEffect == BLEND_DARKEN) {
1458		for (i = 0; i < 512; ++i) {
1459			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1460		}
1461	} else {
1462		for (i = 0; i < 512; ++i) {
1463			renderer->variantPalette[i] = renderer->normalPalette[i];
1464		}
1465	}
1466}
1467
1468static inline unsigned _brighten(unsigned color, int y) {
1469	unsigned c = 0;
1470	unsigned a;
1471#ifdef COLOR_16_BIT
1472	a = color & 0x1F;
1473	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1474
1475#ifdef COLOR_5_6_5
1476	a = color & 0x7C0;
1477	c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1478
1479	a = color & 0xF800;
1480	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1481#else
1482	a = color & 0x3E0;
1483	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1484
1485	a = color & 0x7C00;
1486	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1487#endif
1488#else
1489	a = color & 0xF8;
1490	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1491
1492	a = color & 0xF800;
1493	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1494
1495	a = color & 0xF80000;
1496	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1497#endif
1498	return c;
1499}
1500
1501static inline unsigned _darken(unsigned color, int y) {
1502	unsigned c = 0;
1503	unsigned a;
1504#ifdef COLOR_16_BIT
1505	a = color & 0x1F;
1506	c |= (a - (a * y) / 16) & 0x1F;
1507
1508#ifdef COLOR_5_6_5
1509	a = color & 0x7C0;
1510	c |= (a - (a * y) / 16) & 0x7C0;
1511
1512	a = color & 0xF800;
1513	c |= (a - (a * y) / 16) & 0xF800;
1514#else
1515	a = color & 0x3E0;
1516	c |= (a - (a * y) / 16) & 0x3E0;
1517
1518	a = color & 0x7C00;
1519	c |= (a - (a * y) / 16) & 0x7C00;
1520#endif
1521#else
1522	a = color & 0xF8;
1523	c |= (a - (a * y) / 16) & 0xF8;
1524
1525	a = color & 0xF800;
1526	c |= (a - (a * y) / 16) & 0xF800;
1527
1528	a = color & 0xF80000;
1529	c |= (a - (a * y) / 16) & 0xF80000;
1530#endif
1531	return c;
1532}
1533
1534static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1535	unsigned c = 0;
1536	unsigned a, b;
1537#ifdef COLOR_16_BIT
1538	a = colorA & 0x1F;
1539	b = colorB & 0x1F;
1540	c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1541	if (c & 0x0020) {
1542		c = 0x001F;
1543	}
1544
1545#ifdef COLOR_5_6_5
1546	a = colorA & 0x7C0;
1547	b = colorB & 0x7C0;
1548	c |= ((a * weightA + b * weightB) / 16) & 0xFC0;
1549	if (c & 0x0800) {
1550		c = (c & 0x001F) | 0x07C0;
1551	}
1552
1553	a = colorA & 0xF800;
1554	b = colorB & 0xF800;
1555	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1556	if (c & 0x10000) {
1557		c = (c &0x07FF) | 0xF800;
1558	}
1559#else
1560	a = colorA & 0x3E0;
1561	b = colorB & 0x3E0;
1562	c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1563	if (c & 0x0400) {
1564		c = (c & 0x001F) | 0x03E0;
1565	}
1566
1567	a = colorA & 0x7C00;
1568	b = colorB & 0x7C00;
1569	c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1570	if (c & 0x8000) {
1571		c = (c &0x03FF) | 0x7C00;
1572	}
1573#endif
1574#else
1575	a = colorA & 0xF8;
1576	b = colorB & 0xF8;
1577	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1578	if (c & 0x00000100) {
1579		c = 0x000000F8;
1580	}
1581
1582	a = colorA & 0xF800;
1583	b = colorB & 0xF800;
1584	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1585	if (c & 0x00010000) {
1586		c = (c & 0x000000F8) | 0x0000F800;
1587	}
1588
1589	a = colorA & 0xF80000;
1590	b = colorB & 0xF80000;
1591	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1592	if (c & 0x01000000) {
1593		c = (c & 0x0000F8F8) | 0x00F80000;
1594	}
1595#endif
1596	return c;
1597}