all repos — mgba @ 35a5e83465444a277bd6a753082ec35ec8ebcc0a

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