all repos — mgba @ f09e44062f3117825df934eef05795728a9e2296

mGBA Game Boy Advance Emulator

src/gba/renderers/video-software.c (view raw)

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