all repos — mgba @ c2658ce7a13c50bd05e89ed680ee066b6260036b

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			// TODO: 256-color mosaic
 910		}
 911		return;
 912	}
 913
 914	if (!background->multipalette) {
 915		for (; tileX < tileEnd; ++tileX) {
 916			BACKGROUND_TEXT_SELECT_CHARACTER;
 917			paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4;
 918			palette = &mainPalette[paletteData];
 919			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY;
 920			tileData = ((uint32_t*)renderer->d.vram)[charBase];
 921			if (tileData) {
 922				if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 923					BACKGROUND_DRAW_PIXEL_16;
 924					++pixel;
 925					BACKGROUND_DRAW_PIXEL_16;
 926					++pixel;
 927					BACKGROUND_DRAW_PIXEL_16;
 928					++pixel;
 929					BACKGROUND_DRAW_PIXEL_16;
 930					++pixel;
 931					BACKGROUND_DRAW_PIXEL_16;
 932					++pixel;
 933					BACKGROUND_DRAW_PIXEL_16;
 934					++pixel;
 935					BACKGROUND_DRAW_PIXEL_16;
 936					++pixel;
 937					BACKGROUND_DRAW_PIXEL_16;
 938					++pixel;
 939				} else {
 940					pixel += 7;
 941					BACKGROUND_DRAW_PIXEL_16;
 942					--pixel;
 943					BACKGROUND_DRAW_PIXEL_16;
 944					--pixel;
 945					BACKGROUND_DRAW_PIXEL_16;
 946					--pixel;
 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 += 8;
 957				}
 958			} else {
 959				pixel += 8;
 960			}
 961		}
 962	} else {
 963		for (; tileX < tileEnd; ++tileX) {
 964			BACKGROUND_TEXT_SELECT_CHARACTER;
 965			charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1);
 966			if (!GBA_TEXT_MAP_HFLIP(mapData)) {
 967				tileData = ((uint32_t*)renderer->d.vram)[charBase];
 968				if (tileData) {
 969						BACKGROUND_DRAW_PIXEL_256;
 970						++pixel;
 971						BACKGROUND_DRAW_PIXEL_256;
 972						++pixel;
 973						BACKGROUND_DRAW_PIXEL_256;
 974						++pixel;
 975						BACKGROUND_DRAW_PIXEL_256;
 976						++pixel;
 977				} else {
 978					pixel += 4;
 979				}
 980				tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 981				if (tileData) {
 982						BACKGROUND_DRAW_PIXEL_256;
 983						++pixel;
 984						BACKGROUND_DRAW_PIXEL_256;
 985						++pixel;
 986						BACKGROUND_DRAW_PIXEL_256;
 987						++pixel;
 988						BACKGROUND_DRAW_PIXEL_256;
 989						++pixel;
 990				} else {
 991					pixel += 4;
 992				}
 993			} else {
 994				uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
 995				if (tileData) {
 996					pixel += 3;
 997					BACKGROUND_DRAW_PIXEL_256;
 998					--pixel;
 999					BACKGROUND_DRAW_PIXEL_256;
1000					--pixel;
1001					BACKGROUND_DRAW_PIXEL_256;
1002					--pixel;
1003					BACKGROUND_DRAW_PIXEL_256;
1004				}
1005				pixel += 4;
1006				tileData = ((uint32_t*)renderer->d.vram)[charBase];
1007				if (tileData) {
1008					pixel += 3;
1009					BACKGROUND_DRAW_PIXEL_256;
1010					--pixel;
1011					BACKGROUND_DRAW_PIXEL_256;
1012					--pixel;
1013					BACKGROUND_DRAW_PIXEL_256;
1014					--pixel;
1015					BACKGROUND_DRAW_PIXEL_256;
1016				}
1017				pixel += 4;
1018			}
1019		}
1020	}
1021}
1022
1023#define BACKGROUND_BITMAP_INIT \
1024	(void)(unused); \
1025	int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1026	int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1027	int32_t localX; \
1028	int32_t localY; \
1029	\
1030	int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
1031	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1032	flags |= FLAG_TARGET_2 * background->target2; \
1033	if (!renderer->anyTarget2) { \
1034		flags |= FLAG_FINALIZED; \
1035	} \
1036	int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1037	color_t* palette = renderer->normalPalette; \
1038	if (variant) { \
1039		palette = renderer->variantPalette; \
1040	} \
1041	PREPARE_OBJWIN;
1042
1043#define BACKGROUND_BITMAP_ITERATE(W, H) \
1044	x += background->dx; \
1045	y += background->dy; \
1046	\
1047	if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1048		continue; \
1049	} else { \
1050		localX = x; \
1051		localY = y; \
1052	}
1053
1054static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1055	int sizeAdjusted = 0x8000 << background->size;
1056
1057	BACKGROUND_BITMAP_INIT;
1058
1059	uint32_t screenBase = background->screenBase;
1060	uint32_t charBase = background->charBase;
1061	uint8_t mapData;
1062	uint8_t tileData;
1063
1064	int outX;
1065	uint32_t* pixel;
1066	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1067		x += background->dx;
1068		y += background->dy;
1069
1070		if (background->overflow) {
1071			localX = x & (sizeAdjusted - 1);
1072			localY = y & (sizeAdjusted - 1);
1073		} else if ((x | y) & ~(sizeAdjusted - 1)) {
1074			continue;
1075		} else {
1076			localX = x;
1077			localY = y;
1078		}
1079		mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1080		tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1081
1082		uint32_t current = *pixel;
1083		if (tileData && !(current & FLAG_FINALIZED)) {
1084			if (!objwinSlowPath) {
1085				_composite(renderer, pixel, palette[tileData] | flags, current);
1086			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1087				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1088				_composite(renderer, pixel, currentPalette[tileData] | flags, current);
1089			}
1090		}
1091	}
1092}
1093
1094static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1095	BACKGROUND_BITMAP_INIT;
1096
1097	uint32_t color;
1098
1099	int outX;
1100	uint32_t* pixel;
1101	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1102		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1103
1104		color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1105#ifndef COLOR_16_BIT
1106		unsigned color32;
1107		color32 = 0;
1108		color32 |= (color << 3) & 0xF8;
1109		color32 |= (color << 6) & 0xF800;
1110		color32 |= (color << 9) & 0xF80000;
1111		color = color32;
1112#endif
1113
1114		uint32_t current = *pixel;
1115		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1116			if (!variant) {
1117				_composite(renderer, pixel, color | flags, current);
1118			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1119				_composite(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1120			} else if (renderer->blendEffect == BLEND_DARKEN) {
1121				_composite(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1122			}
1123		}
1124	}
1125}
1126
1127static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1128	BACKGROUND_BITMAP_INIT;
1129
1130	uint16_t color;
1131	uint32_t offset = 0;
1132	if (renderer->dispcnt.frameSelect) {
1133		offset = 0xA000;
1134	}
1135
1136	int outX;
1137	uint32_t* pixel;
1138	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1139		BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1140
1141		color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1142
1143		uint32_t current = *pixel;
1144		if (color && !(current & FLAG_FINALIZED)) {
1145			if (!objwinSlowPath) {
1146				_composite(renderer, pixel, palette[color] | flags, current);
1147			} else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1148				color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1149				_composite(renderer, pixel, currentPalette[color] | flags, current);
1150			}
1151		}
1152	}
1153}
1154
1155static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1156	BACKGROUND_BITMAP_INIT;
1157
1158	uint32_t color;
1159	uint32_t offset = 0;
1160	if (renderer->dispcnt.frameSelect) {
1161		offset = 0xA000;
1162	}
1163
1164	int outX;
1165	uint32_t* pixel;
1166	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1167		BACKGROUND_BITMAP_ITERATE(160, 128);
1168
1169		color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1170#ifndef COLOR_16_BIT
1171		unsigned color32 = 0;
1172		color32 |= (color << 9) & 0xF80000;
1173		color32 |= (color << 3) & 0xF8;
1174		color32 |= (color << 6) & 0xF800;
1175		color = color32;
1176#endif
1177
1178		uint32_t current = *pixel;
1179		if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1180			if (!variant) {
1181				_composite(renderer, pixel, color | flags, current);
1182			} else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1183				_composite(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1184			} else if (renderer->blendEffect == BLEND_DARKEN) {
1185				_composite(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1186			}
1187		}
1188	}
1189}
1190
1191static const int _objSizes[32] = {
1192	8, 8,
1193	16, 16,
1194	32, 32,
1195	64, 64,
1196	16, 8,
1197	32, 8,
1198	32, 16,
1199	64, 32,
1200	8, 16,
1201	8, 32,
1202	16, 32,
1203	32, 64,
1204	0, 0,
1205	0, 0,
1206	0, 0,
1207	0, 0
1208};
1209
1210#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1211	SPRITE_YBASE_ ## DEPTH(inY); \
1212	for (; outX < condition; ++outX, inX += xOffset) { \
1213		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1214			continue; \
1215		} \
1216		SPRITE_XBASE_ ## DEPTH(inX); \
1217		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1218	}
1219
1220#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1221	SPRITE_YBASE_ ## DEPTH(inY); \
1222	if (outX % mosaicH) { \
1223		inX += (mosaicH - (outX % mosaicH)) * xOffset; \
1224		outX += mosaicH - (outX % mosaicH); \
1225	} \
1226	for (; outX < condition; ++outX, inX += xOffset) { \
1227		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1228			continue; \
1229		} \
1230		int localX = inX - xOffset * (outX % mosaicH); \
1231		SPRITE_XBASE_ ## DEPTH(localX); \
1232		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1233	}
1234
1235#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1236	int outX; \
1237	for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1238		if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1239			continue; \
1240		} \
1241		int inX = outX - x; \
1242		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1243		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1244		\
1245		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1246			continue; \
1247		} \
1248		\
1249		SPRITE_YBASE_ ## DEPTH(localY); \
1250		SPRITE_XBASE_ ## DEPTH(localX); \
1251		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1252	}
1253
1254#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1255#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1256
1257#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1258	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1259	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1260	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1261		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1262	}
1263
1264#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1265	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1266	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1267	if (tileData) { \
1268		renderer->row[outX] |= FLAG_OBJWIN; \
1269	}
1270
1271#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1272#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1273
1274#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1275	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1276	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1277	if (tileData && (!(renderer->spriteLayer[outX]) || ((renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags))) { \
1278		renderer->spriteLayer[outX] = palette[tileData] | flags; \
1279	}
1280
1281#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1282	unsigned tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1283	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1284	if (tileData) { \
1285		renderer->row[outX] |= FLAG_OBJWIN; \
1286	}
1287
1288static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1289	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1290	if (sprite->mosaic) {
1291		int mosaicV = renderer->mosaic.objV + 1;
1292		y -= y % mosaicV;
1293	}
1294	if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1295		return 0;
1296	}
1297	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1298	int start = renderer->start;
1299	int end = renderer->end;
1300	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1301	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1302	flags |= FLAG_TARGET_2 *renderer->target2Obj;
1303	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1304	int x = sprite->x;
1305	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1306	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1307	if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1308		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1309		variant = 0;
1310	}
1311	color_t* palette = &renderer->normalPalette[0x100];
1312	if (variant) {
1313		palette = &renderer->variantPalette[0x100];
1314	}
1315
1316	int outX = x >= start ? x : start;
1317	int condition = x + width;
1318	int mosaicH = 1;
1319	if (sprite->mosaic) {
1320		mosaicH = renderer->mosaic.objH + 1;
1321		if (condition % mosaicH) {
1322			condition += mosaicH - (condition % mosaicH);
1323		}
1324	}
1325	int inY = y - sprite->y;
1326	if (sprite->y + height - 256 >= 0) {
1327		inY += 256;
1328	}
1329	if (sprite->vflip) {
1330		inY = height - inY - 1;
1331	}
1332	if (end < condition) {
1333		condition = end;
1334	}
1335	int inX = outX - x;
1336	int xOffset = 1;
1337	if (sprite->hflip) {
1338		inX = width - inX - 1;
1339		xOffset = -1;
1340	}
1341	if (!sprite->multipalette) {
1342		palette = &palette[sprite->palette << 4];
1343		if (flags & FLAG_OBJWIN) {
1344			SPRITE_NORMAL_LOOP(16, OBJWIN);
1345		} else if (sprite->mosaic) {
1346			SPRITE_MOSAIC_LOOP(16, NORMAL);
1347		} else {
1348			SPRITE_NORMAL_LOOP(16, NORMAL);
1349		}
1350	} else {
1351		if (flags & FLAG_OBJWIN) {
1352			SPRITE_NORMAL_LOOP(256, OBJWIN);
1353		} else if (sprite->mosaic) {
1354			SPRITE_MOSAIC_LOOP(256, NORMAL);
1355		} else {
1356			SPRITE_NORMAL_LOOP(256, NORMAL);
1357		}
1358	}
1359	return 1;
1360}
1361
1362static int _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1363	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1364	int totalHeight = height << sprite->doublesize;
1365	if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1366		return 0;
1367	}
1368	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1369	int totalWidth = width << sprite->doublesize;
1370	int start = renderer->start;
1371	int end = renderer->end;
1372	uint32_t flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1373	flags |= FLAG_TARGET_1 * ((renderer->currentWindow.blendEnable && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1374	flags |= FLAG_TARGET_2 * renderer->target2Obj;
1375	flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1376	int x = sprite->x;
1377	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1378	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1379	int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1380	if (sprite->mode == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1381		// Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1382		variant = 0;
1383	}
1384	color_t* palette = &renderer->normalPalette[0x100];
1385	if (variant) {
1386		palette = &renderer->variantPalette[0x100];
1387	}
1388	int inY = y - sprite->y;
1389	if (inY < 0) {
1390		inY += 256;
1391	}
1392	if (!sprite->multipalette) {
1393		palette = &palette[sprite->palette << 4];
1394		if (flags & FLAG_OBJWIN) {
1395			SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1396		} else {
1397			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1398		}
1399	} else {
1400		if (flags & FLAG_OBJWIN) {
1401			SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1402		} else {
1403			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1404		}
1405	}
1406	return 1;
1407}
1408
1409static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1410	int x;
1411	uint32_t* pixel = renderer->row;
1412	for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1413		uint32_t color = renderer->spriteLayer[x];
1414		uint32_t current = *pixel;
1415		if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1416			_composite(renderer, pixel, color & ~FLAG_FINALIZED, current);
1417		}
1418	}
1419}
1420
1421static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1422	int i;
1423	if (renderer->blendEffect == BLEND_BRIGHTEN) {
1424		for (i = 0; i < 512; ++i) {
1425			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1426		}
1427	} else if (renderer->blendEffect == BLEND_DARKEN) {
1428		for (i = 0; i < 512; ++i) {
1429			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1430		}
1431	} else {
1432		for (i = 0; i < 512; ++i) {
1433			renderer->variantPalette[i] = renderer->normalPalette[i];
1434		}
1435	}
1436}
1437
1438static inline unsigned _brighten(unsigned color, int y) {
1439	unsigned c = 0;
1440	unsigned a;
1441#ifdef COLOR_16_BIT
1442	a = color & 0x1F;
1443	c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1444
1445#ifdef COLOR_5_6_5
1446	a = color & 0x7C0;
1447	c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1448
1449	a = color & 0xF800;
1450	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1451#else
1452	a = color & 0x3E0;
1453	c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1454
1455	a = color & 0x7C00;
1456	c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1457#endif
1458#else
1459	a = color & 0xF8;
1460	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1461
1462	a = color & 0xF800;
1463	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1464
1465	a = color & 0xF80000;
1466	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1467#endif
1468	return c;
1469}
1470
1471static inline unsigned _darken(unsigned color, int y) {
1472	unsigned c = 0;
1473	unsigned a;
1474#ifdef COLOR_16_BIT
1475	a = color & 0x1F;
1476	c |= (a - (a * y) / 16) & 0x1F;
1477
1478#ifdef COLOR_5_6_5
1479	a = color & 0x7C0;
1480	c |= (a - (a * y) / 16) & 0x7C0;
1481
1482	a = color & 0xF800;
1483	c |= (a - (a * y) / 16) & 0xF800;
1484#else
1485	a = color & 0x3E0;
1486	c |= (a - (a * y) / 16) & 0x3E0;
1487
1488	a = color & 0x7C00;
1489	c |= (a - (a * y) / 16) & 0x7C00;
1490#endif
1491#else
1492	a = color & 0xF8;
1493	c |= (a - (a * y) / 16) & 0xF8;
1494
1495	a = color & 0xF800;
1496	c |= (a - (a * y) / 16) & 0xF800;
1497
1498	a = color & 0xF80000;
1499	c |= (a - (a * y) / 16) & 0xF80000;
1500#endif
1501	return c;
1502}
1503
1504static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1505	unsigned c = 0;
1506	unsigned a, b;
1507#ifdef COLOR_16_BIT
1508	a = colorA & 0x1F;
1509	b = colorB & 0x1F;
1510	c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1511	if (c & 0x0020) {
1512		c = 0x001F;
1513	}
1514
1515#ifdef COLOR_5_6_5
1516	a = colorA & 0x7C0;
1517	b = colorB & 0x7C0;
1518	c |= ((a * weightA + b * weightB) / 16) & 0xFC0;
1519	if (c & 0x0800) {
1520		c = (c & 0x001F) | 0x07C0;
1521	}
1522
1523	a = colorA & 0xF800;
1524	b = colorB & 0xF800;
1525	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1526	if (c & 0x10000) {
1527		c = (c &0x07FF) | 0xF800;
1528	}
1529#else
1530	a = colorA & 0x3E0;
1531	b = colorB & 0x3E0;
1532	c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1533	if (c & 0x0400) {
1534		c = (c & 0x001F) | 0x03E0;
1535	}
1536
1537	a = colorA & 0x7C00;
1538	b = colorB & 0x7C00;
1539	c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1540	if (c & 0x8000) {
1541		c = (c &0x03FF) | 0x7C00;
1542	}
1543#endif
1544#else
1545	a = colorA & 0xF8;
1546	b = colorB & 0xF8;
1547	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1548	if (c & 0x00000100) {
1549		c = 0x000000F8;
1550	}
1551
1552	a = colorA & 0xF800;
1553	b = colorB & 0xF800;
1554	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1555	if (c & 0x00010000) {
1556		c = (c & 0x000000F8) | 0x0000F800;
1557	}
1558
1559	a = colorA & 0xF80000;
1560	b = colorB & 0xF80000;
1561	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1562	if (c & 0x01000000) {
1563		c = (c & 0x0000F8F8) | 0x00F80000;
1564	}
1565#endif
1566	return c;
1567}