all repos — mgba @ 3439fa5bf41eb9a8f71e73938ff209804e54eb84

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