all repos — mgba @ 34ddb09516c584e7a47cf61e0ad3fe00c7e13c5a

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