all repos — mgba @ 25cc40f3e16871faf2950bd9e524e5a2d4923501

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