all repos — mgba @ 09b4a4a29a6393d2c5e2671810d8b046d46fdf13

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