all repos — mgba @ e7be40e80ccb6dbeee0b79c97f349859b1b4afb8

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "gba/renderers/software-private.h"
  7
  8#include <mgba/core/cache-set.h>
  9#include <mgba/internal/arm/macros.h>
 10#include <mgba/internal/gba/io.h>
 11#include <mgba/internal/gba/renderers/cache-set.h>
 12
 13#include <mgba-util/arm-algo.h>
 14#include <mgba-util/memory.h>
 15
 16#define DIRTY_SCANLINE(R, Y) R->scanlineDirty[Y >> 5] |= (1 << (Y & 0x1F))
 17#define CLEAN_SCANLINE(R, Y) R->scanlineDirty[Y >> 5] &= ~(1 << (Y & 0x1F))
 18
 19static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
 20static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
 21static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer);
 22static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
 23static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 24static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 25static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 26static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 27static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
 28static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
 29static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
 30
 31static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
 32static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
 33static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
 34static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
 35static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
 36static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
 37static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
 38
 39static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
 40
 41static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
 42
 43static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y);
 44static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win);
 45
 46void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
 47	renderer->d.init = GBAVideoSoftwareRendererInit;
 48	renderer->d.reset = GBAVideoSoftwareRendererReset;
 49	renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
 50	renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
 51	renderer->d.writeVRAM = GBAVideoSoftwareRendererWriteVRAM;
 52	renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
 53	renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
 54	renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
 55	renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
 56	renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels;
 57	renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels;
 58
 59	renderer->d.disableBG[0] = false;
 60	renderer->d.disableBG[1] = false;
 61	renderer->d.disableBG[2] = false;
 62	renderer->d.disableBG[3] = false;
 63	renderer->d.disableOBJ = false;
 64	renderer->tileStride = 0x20;
 65	renderer->bitmapStride = 0;
 66	renderer->combinedObjSort = false;
 67	renderer->masterEnd = GBA_VIDEO_HORIZONTAL_PIXELS;
 68	renderer->masterHeight = GBA_VIDEO_VERTICAL_PIXELS;
 69	renderer->masterScanlines = VIDEO_VERTICAL_TOTAL_PIXELS;
 70
 71	renderer->temporaryBuffer = 0;
 72}
 73
 74static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
 75	GBAVideoSoftwareRendererReset(renderer);
 76
 77	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 78
 79	int y;
 80	for (y = 0; y < softwareRenderer->masterHeight; ++y) {
 81		color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
 82		int x;
 83		for (x = 0; x < softwareRenderer->masterEnd; ++x) {
 84			row[x] = GBA_COLOR_WHITE;
 85		}
 86	}
 87}
 88
 89static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
 90	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 91	int i;
 92
 93	softwareRenderer->dispcnt = 0x0080;
 94
 95	softwareRenderer->target1Obj = 0;
 96	softwareRenderer->target1Bd = 0;
 97	softwareRenderer->target2Obj = 0;
 98	softwareRenderer->target2Bd = 0;
 99	softwareRenderer->blendEffect = BLEND_NONE;
100	softwareRenderer->masterBright = 0;
101	softwareRenderer->masterBrightY = 0;
102	for (i = 0; i < 1024; i += 2) {
103		uint16_t entry;
104		LOAD_16(entry, i, softwareRenderer->d.palette);
105		GBAVideoSoftwareRendererWritePalette(renderer, i, entry);
106	}
107	softwareRenderer->objExtPalette = NULL;
108	softwareRenderer->objExtVariantPalette = NULL;
109	softwareRenderer->blendDirty = false;
110	_updatePalettes(softwareRenderer);
111
112	softwareRenderer->blda = 0;
113	softwareRenderer->bldb = 0;
114	softwareRenderer->bldy = 0;
115
116	softwareRenderer->winN[0] = (struct WindowN) { .control = { .priority = 0 } };
117	softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } };
118	softwareRenderer->objwin = (struct WindowControl) { .priority = 2 };
119	softwareRenderer->winout = (struct WindowControl) { .priority = 3 };
120	softwareRenderer->oamDirty = 1;
121	softwareRenderer->oamMax = 0;
122
123	softwareRenderer->mosaic = 0;
124	softwareRenderer->nextY = 0;
125
126	softwareRenderer->objOffsetX = 0;
127	softwareRenderer->objOffsetY = 0;
128
129	memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
130	memset(softwareRenderer->cache, 0, sizeof(softwareRenderer->cache));
131	memset(softwareRenderer->nextIo, 0, sizeof(softwareRenderer->nextIo));
132
133	for (i = 0; i < 4; ++i) {
134		struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
135		bg->index = i;
136		bg->enabled = 0;
137		bg->priority = 0;
138		bg->charBase = 0;
139		bg->mosaic = 0;
140		bg->multipalette = 0;
141		bg->screenBase = 0;
142		bg->overflow = 0;
143		bg->size = 0;
144		bg->target1 = 0;
145		bg->target2 = 0;
146		bg->x = 0;
147		bg->y = 0;
148		bg->refx = 0;
149		bg->refy = 0;
150		bg->dx = 256;
151		bg->dmx = 0;
152		bg->dy = 0;
153		bg->dmy = 256;
154		bg->sx = 0;
155		bg->sy = 0;
156		bg->yCache = -1;
157		bg->extPalette = NULL;
158		bg->variantPalette = NULL;
159		bg->offsetX = 0;
160		bg->offsetY = 0;
161	}
162}
163
164static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
165	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
166	UNUSED(softwareRenderer);
167}
168
169static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
170	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
171	if (renderer->cache) {
172		GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
173	}
174
175	switch (address) {
176	case REG_DISPCNT:
177		value &= 0xFFF7;
178		softwareRenderer->dispcnt = value;
179		GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
180		break;
181	case REG_BG0CNT:
182		value &= 0xDFFF;
183		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
184		break;
185	case REG_BG1CNT:
186		value &= 0xDFFF;
187		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
188		break;
189	case REG_BG2CNT:
190		value &= 0xFFFF;
191		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
192		break;
193	case REG_BG3CNT:
194		value &= 0xFFFF;
195		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
196		break;
197	case REG_BG0HOFS:
198		value &= 0x01FF;
199		softwareRenderer->bg[0].x = value;
200		break;
201	case REG_BG0VOFS:
202		value &= 0x01FF;
203		softwareRenderer->bg[0].y = value;
204		break;
205	case REG_BG1HOFS:
206		value &= 0x01FF;
207		softwareRenderer->bg[1].x = value;
208		break;
209	case REG_BG1VOFS:
210		value &= 0x01FF;
211		softwareRenderer->bg[1].y = value;
212		break;
213	case REG_BG2HOFS:
214		value &= 0x01FF;
215		softwareRenderer->bg[2].x = value;
216		break;
217	case REG_BG2VOFS:
218		value &= 0x01FF;
219		softwareRenderer->bg[2].y = value;
220		break;
221	case REG_BG3HOFS:
222		value &= 0x01FF;
223		softwareRenderer->bg[3].x = value;
224		break;
225	case REG_BG3VOFS:
226		value &= 0x01FF;
227		softwareRenderer->bg[3].y = value;
228		break;
229	case REG_BG2PA:
230		softwareRenderer->bg[2].dx = value;
231		break;
232	case REG_BG2PB:
233		softwareRenderer->bg[2].dmx = value;
234		break;
235	case REG_BG2PC:
236		softwareRenderer->bg[2].dy = value;
237		break;
238	case REG_BG2PD:
239		softwareRenderer->bg[2].dmy = value;
240		break;
241	case REG_BG2X_LO:
242		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
243		if (softwareRenderer->bg[2].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[0][0]) {
244			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
245		}
246		break;
247	case REG_BG2X_HI:
248		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
249		if (softwareRenderer->bg[2].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[0][0]) {
250			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
251		}
252		break;
253	case REG_BG2Y_LO:
254		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
255		if (softwareRenderer->bg[2].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[0][1]) {
256			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
257		}
258		break;
259	case REG_BG2Y_HI:
260		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
261		if (softwareRenderer->bg[2].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[0][1]) {
262			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
263		}
264		break;
265	case REG_BG3PA:
266		softwareRenderer->bg[3].dx = value;
267		break;
268	case REG_BG3PB:
269		softwareRenderer->bg[3].dmx = value;
270		break;
271	case REG_BG3PC:
272		softwareRenderer->bg[3].dy = value;
273		break;
274	case REG_BG3PD:
275		softwareRenderer->bg[3].dmy = value;
276		break;
277	case REG_BG3X_LO:
278		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
279		if (softwareRenderer->bg[3].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[1][0]) {
280			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
281		}
282		break;
283	case REG_BG3X_HI:
284		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
285		if (softwareRenderer->bg[3].sx != softwareRenderer->cache[softwareRenderer->nextY].scale[1][0]) {
286			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
287		}
288		break;
289	case REG_BG3Y_LO:
290		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
291		if (softwareRenderer->bg[3].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[1][1]) {
292			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
293		}
294		break;
295	case REG_BG3Y_HI:
296		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
297		if (softwareRenderer->bg[3].sy != softwareRenderer->cache[softwareRenderer->nextY].scale[1][1]) {
298			DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
299		}
300		break;
301	case REG_BLDCNT:
302		GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
303		value &= 0x3FFF;
304		break;
305	case REG_BLDALPHA:
306		softwareRenderer->blda = value & 0x1F;
307		if (softwareRenderer->blda > 0x10) {
308			softwareRenderer->blda = 0x10;
309		}
310		softwareRenderer->bldb = (value >> 8) & 0x1F;
311		if (softwareRenderer->bldb > 0x10) {
312			softwareRenderer->bldb = 0x10;
313		}
314		softwareRenderer->blendDirty = true;
315		value &= 0x1F1F;
316		break;
317	case REG_BLDY:
318		value &= 0x1F;
319		if (value > 0x10) {
320			value = 0x10;
321		}
322		if (softwareRenderer->bldy != value) {
323			softwareRenderer->bldy = value;
324			softwareRenderer->blendDirty = true;
325		}
326		break;
327	case REG_WIN0H:
328		softwareRenderer->winN[0].h.end = value & 0xFF;
329		softwareRenderer->winN[0].h.start = value >> 8;
330		if (softwareRenderer->winN[0].h.start > softwareRenderer->masterEnd && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
331			softwareRenderer->winN[0].h.start = 0;
332		}
333		if (softwareRenderer->winN[0].h.end > softwareRenderer->masterEnd) {
334			softwareRenderer->winN[0].h.end = softwareRenderer->masterEnd;
335			if (softwareRenderer->winN[0].h.start > softwareRenderer->masterEnd) {
336				softwareRenderer->winN[0].h.start = softwareRenderer->masterEnd;
337			}
338		}
339		if (softwareRenderer->masterEnd > 0xFF && softwareRenderer->winN[0].h.end == (softwareRenderer->masterEnd & 0xFF) && softwareRenderer->winN[0].h.start != softwareRenderer->winN[0].h.end) {
340			softwareRenderer->winN[0].h.end = softwareRenderer->masterEnd;
341		}
342		break;
343	case REG_WIN1H:
344		softwareRenderer->winN[1].h.end = value & 0xFF;
345		softwareRenderer->winN[1].h.start = value >> 8;
346		if (softwareRenderer->winN[1].h.start > softwareRenderer->masterEnd && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
347			softwareRenderer->winN[1].h.start = 0;
348		}
349		if (softwareRenderer->winN[1].h.end > softwareRenderer->masterEnd) {
350			softwareRenderer->winN[1].h.end = softwareRenderer->masterEnd;
351			if (softwareRenderer->winN[1].h.start > softwareRenderer->masterEnd) {
352				softwareRenderer->winN[1].h.start = softwareRenderer->masterEnd;
353			}
354		}
355		if (softwareRenderer->masterEnd > 0xFF && softwareRenderer->winN[1].h.end == (softwareRenderer->masterEnd & 0xFF) && softwareRenderer->winN[1].h.start != softwareRenderer->winN[1].h.end) {
356			softwareRenderer->winN[1].h.end = softwareRenderer->masterEnd;
357		}
358		break;
359	case REG_WIN0V:
360		softwareRenderer->winN[0].v.end = value & 0xFF;
361		softwareRenderer->winN[0].v.start = value >> 8;
362		if (softwareRenderer->winN[0].v.start > softwareRenderer->masterHeight && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
363			softwareRenderer->winN[0].v.start = 0;
364		}
365		if (softwareRenderer->winN[0].v.end > softwareRenderer->masterHeight) {
366			softwareRenderer->winN[0].v.end = softwareRenderer->masterHeight;
367			if (softwareRenderer->winN[0].v.start > softwareRenderer->masterHeight) {
368				softwareRenderer->winN[0].v.start = softwareRenderer->masterHeight;
369			}
370		}
371		break;
372	case REG_WIN1V:
373		softwareRenderer->winN[1].v.end = value & 0xFF;
374		softwareRenderer->winN[1].v.start = value >> 8;
375		if (softwareRenderer->winN[1].v.start > softwareRenderer->masterHeight && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
376			softwareRenderer->winN[1].v.start = 0;
377		}
378		if (softwareRenderer->winN[1].v.end > softwareRenderer->masterHeight) {
379			softwareRenderer->winN[1].v.end = softwareRenderer->masterHeight;
380			if (softwareRenderer->winN[1].v.start > softwareRenderer->masterHeight) {
381				softwareRenderer->winN[1].v.start = softwareRenderer->masterHeight;
382			}
383		}
384		break;
385	case REG_WININ:
386		value &= 0x3F3F;
387		softwareRenderer->winN[0].control.packed = value;
388		softwareRenderer->winN[1].control.packed = value >> 8;
389		break;
390	case REG_WINOUT:
391		value &= 0x3F3F;
392		softwareRenderer->winout.packed = value;
393		softwareRenderer->objwin.packed = value >> 8;
394		break;
395	case REG_MOSAIC:
396		softwareRenderer->mosaic = value;
397		break;
398	case REG_GREENSWP:
399		mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);
400		break;
401	default:
402		mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
403	}
404	softwareRenderer->nextIo[address >> 1] = value;
405	if (softwareRenderer->cache[softwareRenderer->nextY].io[address >> 1] != value) {
406		softwareRenderer->cache[softwareRenderer->nextY].io[address >> 1] = value;
407		DIRTY_SCANLINE(softwareRenderer, softwareRenderer->nextY);
408	}
409	return value;
410}
411
412static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
413	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
414	if (renderer->cache) {
415		mCacheSetWriteVRAM(renderer->cache, address);
416	}
417	memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
418	softwareRenderer->bg[0].yCache = -1;
419	softwareRenderer->bg[1].yCache = -1;
420	softwareRenderer->bg[2].yCache = -1;
421	softwareRenderer->bg[3].yCache = -1;
422}
423
424static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
425	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
426	UNUSED(oam);
427	softwareRenderer->oamDirty = 1;
428	memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
429}
430
431static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
432	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
433	color_t color = mColorFrom555(value);
434	softwareRenderer->normalPalette[address >> 1] = color;
435	if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
436		softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
437	} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
438		softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
439	}
440	if (renderer->cache) {
441		mCacheSetWritePalette(renderer->cache, address >> 1, color);
442	}
443	memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty));
444}
445
446static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {
447	if (win->v.end >= win->v.start) {
448		if (y >= win->v.end) {
449			return;
450		}
451		if (y < win->v.start) {
452			return;
453		}
454	} else if (y >= win->v.end && y < win->v.start) {
455		return;
456	}
457	if (win->h.end > softwareRenderer->masterEnd || win->h.end < win->h.start) {
458		struct WindowN splits[2] = { *win, *win };
459		splits[0].h.start = 0;
460		splits[1].h.end = softwareRenderer->masterEnd;
461		_breakWindowInner(softwareRenderer, &splits[0]);
462		_breakWindowInner(softwareRenderer, &splits[1]);
463	} else {
464		_breakWindowInner(softwareRenderer, win);
465	}
466}
467
468static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
469	int activeWindow;
470	int startX = 0;
471	if (win->h.end > 0) {
472		for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
473			if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
474				// Insert a window before the end of the active window
475				struct Window oldWindow = softwareRenderer->windows[activeWindow];
476				if (win->h.start > startX) {
477					// And after the start of the active window
478					int nextWindow = softwareRenderer->nWindows;
479					++softwareRenderer->nWindows;
480					for (; nextWindow > activeWindow; --nextWindow) {
481						softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
482					}
483					softwareRenderer->windows[activeWindow].endX = win->h.start;
484					++activeWindow;
485				}
486				softwareRenderer->windows[activeWindow].control = win->control;
487				softwareRenderer->windows[activeWindow].endX = win->h.end;
488				if (win->h.end >= oldWindow.endX) {
489					// Trim off extra windows we've overwritten
490					for (++activeWindow; softwareRenderer->nWindows > activeWindow + 1 && win->h.end >= softwareRenderer->windows[activeWindow].endX; ++activeWindow) {
491						if (VIDEO_CHECKS && activeWindow >= MAX_WINDOW) {
492							mLOG(GBA_VIDEO, FATAL, "Out of bounds window write will occur");
493							return;
494						}
495						softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
496						--softwareRenderer->nWindows;
497					}
498				} else {
499					++activeWindow;
500					int nextWindow = softwareRenderer->nWindows;
501					++softwareRenderer->nWindows;
502					for (; nextWindow > activeWindow; --nextWindow) {
503						softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
504					}
505					softwareRenderer->windows[activeWindow] = oldWindow;
506				}
507				break;
508			}
509			startX = softwareRenderer->windows[activeWindow].endX;
510		}
511	}
512#ifdef DEBUG
513	if (softwareRenderer->nWindows > MAX_WINDOW) {
514		mLOG(GBA_VIDEO, FATAL, "Out of bounds window write occurred!");
515	}
516#endif
517}
518
519static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
520	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
521
522	if (y == GBA_VIDEO_VERTICAL_PIXELS - 1) {
523		softwareRenderer->nextY = 0;
524	} else {
525		softwareRenderer->nextY = y + 1;
526	}
527
528	bool dirty = softwareRenderer->scanlineDirty[y >> 5] & (1 << (y & 0x1F));
529	if (memcmp(softwareRenderer->nextIo, softwareRenderer->cache[y].io, sizeof(softwareRenderer->nextIo))) {
530		memcpy(softwareRenderer->cache[y].io, softwareRenderer->nextIo, sizeof(softwareRenderer->nextIo));
531		dirty = true;
532	}
533
534	if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) {
535		if (softwareRenderer->cache[y].scale[0][0] != softwareRenderer->bg[2].sx ||
536		    softwareRenderer->cache[y].scale[0][1] != softwareRenderer->bg[2].sy ||
537		    softwareRenderer->cache[y].scale[1][0] != softwareRenderer->bg[3].sx ||
538		    softwareRenderer->cache[y].scale[1][1] != softwareRenderer->bg[3].sy) {
539			dirty = true;
540		}
541	}
542	softwareRenderer->cache[y].scale[0][0] = softwareRenderer->bg[2].sx;
543	softwareRenderer->cache[y].scale[0][1] = softwareRenderer->bg[2].sy;
544	softwareRenderer->cache[y].scale[1][0] = softwareRenderer->bg[3].sx;
545	softwareRenderer->cache[y].scale[1][1] = softwareRenderer->bg[3].sy;
546
547	if (!dirty) {
548		if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) {
549			softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
550			softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
551			softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
552			softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
553		}
554		return;
555	}
556
557	CLEAN_SCANLINE(softwareRenderer, y);
558
559	color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
560	if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
561		int x;
562		for (x = 0; x < softwareRenderer->masterEnd; ++x) {
563			row[x] = GBA_COLOR_WHITE;
564		}
565		return;
566	}
567
568	uint16_t* objVramBase = softwareRenderer->d.vramOBJ[0];
569	if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) >= 3) {
570		softwareRenderer->d.vramOBJ[0] = NULL; // OBJ VRAM bottom is blocked in bitmap modes
571	}
572
573	GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
574	int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
575	softwareRenderer->d.vramOBJ[0] = objVramBase;
576	if (softwareRenderer->blendDirty) {
577		_updatePalettes(softwareRenderer);
578		softwareRenderer->blendDirty = false;
579	}
580
581	int w;
582	unsigned priority;
583	for (priority = 0; priority < 4; ++priority) {
584		softwareRenderer->end = 0;
585		for (w = 0; w < softwareRenderer->nWindows; ++w) {
586			softwareRenderer->start = softwareRenderer->end;
587			softwareRenderer->end = softwareRenderer->windows[w].endX;
588			softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
589			if (spriteLayers & (1 << priority)) {
590				GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
591			}
592			if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
593				GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
594			}
595			if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) < 2) {
596				GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
597			}
598			if (TEST_LAYER_ENABLED(2)) {
599				switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
600				case 0:
601					GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
602					break;
603				case 1:
604				case 2:
605					GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
606					break;
607				case 3:
608					GBAVideoSoftwareRendererDrawBackgroundMode3(softwareRenderer, &softwareRenderer->bg[2], y);
609					break;
610				case 4:
611					GBAVideoSoftwareRendererDrawBackgroundMode4(softwareRenderer, &softwareRenderer->bg[2], y);
612					break;
613				case 5:
614					GBAVideoSoftwareRendererDrawBackgroundMode5(softwareRenderer, &softwareRenderer->bg[2], y);
615					break;
616				}
617			}
618			if (TEST_LAYER_ENABLED(3)) {
619				switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
620				case 0:
621					GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
622					break;
623				case 2:
624					GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
625					break;
626				}
627			}
628		}
629	}
630	if (softwareRenderer->target1Obj && (softwareRenderer->blendEffect == BLEND_DARKEN || softwareRenderer->blendEffect == BLEND_BRIGHTEN)) {
631		int x = 0;
632		uint32_t mask = FLAG_REBLEND | FLAG_TARGET_1 | FLAG_IS_BACKGROUND;
633		uint32_t match = FLAG_REBLEND;
634		if (GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
635			mask |= FLAG_OBJWIN;
636			if (GBAWindowControlIsBlendEnable(softwareRenderer->objwin.packed)) {
637				match |= FLAG_OBJWIN;
638			}
639		}
640		for (w = 0; w < softwareRenderer->nWindows; ++w) {
641			if (!GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
642				continue;
643			}
644			int end = softwareRenderer->windows[w].endX;
645			if (softwareRenderer->blendEffect == BLEND_DARKEN) {
646				for (; x < end; ++x) {
647					uint32_t color = softwareRenderer->row[x];
648					if ((color & mask) == match) {
649						softwareRenderer->row[x] = _darken(color, softwareRenderer->bldy);
650					}
651				}
652			} else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
653				for (; x < end; ++x) {
654					uint32_t color = softwareRenderer->row[x];
655					if ((color & mask) == match) {
656						softwareRenderer->row[x] = _brighten(color, softwareRenderer->bldy);
657					}
658				}
659			}
660		}
661	}
662	if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) {
663		softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
664		softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
665		softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
666		softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
667	}
668
669	if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) {
670		++softwareRenderer->bg[0].enabled;
671		DIRTY_SCANLINE(softwareRenderer, y);
672	}
673	if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) {
674		++softwareRenderer->bg[1].enabled;
675		DIRTY_SCANLINE(softwareRenderer, y);
676	}
677	if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) {
678		++softwareRenderer->bg[2].enabled;
679		DIRTY_SCANLINE(softwareRenderer, y);
680	}
681	if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) {
682		++softwareRenderer->bg[3].enabled;
683		DIRTY_SCANLINE(softwareRenderer, y);
684	}
685
686	GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
687
688#ifdef COLOR_16_BIT
689	size_t x;
690	for (x = 0; x < softwareRenderer->masterEnd; ++x) {
691		row[x] = softwareRenderer->row[x];
692		row[x + 1] = softwareRenderer->row[x + 1];
693		row[x + 2] = softwareRenderer->row[x + 2];
694		row[x + 3] = softwareRenderer->row[x + 3];
695	}
696#else
697	memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
698#endif
699}
700
701static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
702	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
703
704	softwareRenderer->nextY = 0;
705	if (softwareRenderer->temporaryBuffer) {
706		mappedMemoryFree(softwareRenderer->temporaryBuffer, softwareRenderer->masterEnd * softwareRenderer->masterHeight * 4);
707		softwareRenderer->temporaryBuffer = 0;
708	}
709	softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
710	softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
711	softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
712	softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
713
714	if (softwareRenderer->bg[0].enabled > 0) {
715		softwareRenderer->bg[0].enabled = 4;
716	}
717	if (softwareRenderer->bg[1].enabled > 0) {
718		softwareRenderer->bg[1].enabled = 4;
719	}
720	if (softwareRenderer->bg[2].enabled > 0) {
721		softwareRenderer->bg[2].enabled = 4;
722	}
723	if (softwareRenderer->bg[3].enabled > 0) {
724		softwareRenderer->bg[3].enabled = 4;
725	}
726}
727
728static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
729	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
730	*stride = softwareRenderer->outputBufferStride;
731	*pixels = softwareRenderer->outputBuffer;
732}
733
734static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
735	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
736
737	const color_t* colorPixels = pixels;
738	int i;
739	for (i = 0; i < softwareRenderer->masterHeight; ++i) {
740		memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], softwareRenderer->masterEnd * BYTES_PER_PIXEL);
741	}
742}
743
744static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool active) {
745	int wasActive = renderer->bg[bg].enabled;
746	if (!active) {
747		renderer->bg[bg].enabled = 0;
748	} else if (!wasActive && active) {
749		if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
750			// TODO: Investigate in more depth how switching background works in different modes
751			renderer->bg[bg].enabled = 4;
752		} else {
753			renderer->bg[bg].enabled = 1;
754		}
755	}
756}
757
758static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
759	_enableBg(renderer, 0, GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt));
760	_enableBg(renderer, 1, GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt));
761	_enableBg(renderer, 2, GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt));
762	_enableBg(renderer, 3, GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt));
763}
764
765static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
766	UNUSED(renderer);
767	bg->priority = GBARegisterBGCNTGetPriority(value);
768	bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14;
769	bg->charBase &= 0xC000;
770	bg->mosaic = GBARegisterBGCNTGetMosaic(value);
771	bg->multipalette = GBARegisterBGCNTGet256Color(value);
772	bg->screenBase &= ~0xF800;
773	bg->screenBase |= GBARegisterBGCNTGetScreenBase(value) << 11;
774	bg->overflow = GBARegisterBGCNTGetOverflow(value);
775	bg->size = GBARegisterBGCNTGetSize(value);
776	bg->control = value;
777}
778
779static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
780	bg->refx = (bg->refx & 0xFFFF0000) | value;
781	bg->sx = bg->refx;
782}
783
784static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
785	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
786	bg->refx <<= 4;
787	bg->refx >>= 4;
788	bg->sx = bg->refx;
789}
790
791static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
792	bg->refy = (bg->refy & 0xFFFF0000) | value;
793	bg->sy = bg->refy;
794}
795
796static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
797	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
798	bg->refy <<= 4;
799	bg->refy >>= 4;
800	bg->sy = bg->refy;
801}
802
803static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
804	enum GBAVideoBlendEffect oldEffect = renderer->blendEffect;
805
806	renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
807	renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
808	renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
809	renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
810	renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
811	renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
812	renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
813	renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
814
815	renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
816	renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
817	renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
818	renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
819	renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
820
821	if (oldEffect != renderer->blendEffect) {
822		renderer->blendDirty = true;
823	}
824}
825
826void GBAVideoSoftwareRendererPreprocessBuffer(struct GBAVideoSoftwareRenderer* softwareRenderer, int y) {
827	int x;
828	int masterEnd = softwareRenderer->masterEnd;
829	for (x = 0; x < masterEnd; x += 4) {
830		softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
831		softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
832		softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
833		softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
834	}
835
836	softwareRenderer->windows[0].endX = softwareRenderer->masterEnd;
837	softwareRenderer->nWindows = 1;
838	if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
839		softwareRenderer->windows[0].control = softwareRenderer->winout;
840		if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt)) {
841			_breakWindow(softwareRenderer, &softwareRenderer->winN[1], y);
842		}
843		if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt)) {
844			_breakWindow(softwareRenderer, &softwareRenderer->winN[0], y);
845		}
846	} else {
847		softwareRenderer->windows[0].control.packed = 0xFF;
848	}
849
850	GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
851
852	int w;
853	x = 0;
854	for (w = 0; w < softwareRenderer->nWindows; ++w) {
855		// TOOD: handle objwin on backdrop
856		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
857		if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
858			backdrop |= softwareRenderer->normalPalette[0];
859		} else {
860			backdrop |= softwareRenderer->variantPalette[0];
861		}
862		int end = softwareRenderer->windows[w].endX;
863		for (; x & 3; ++x) {
864			softwareRenderer->row[x] = backdrop;
865		}
866		for (; x < end - 3; x += 4) {
867			softwareRenderer->row[x] = backdrop;
868			softwareRenderer->row[x + 1] = backdrop;
869			softwareRenderer->row[x + 2] = backdrop;
870			softwareRenderer->row[x + 3] = backdrop;
871		}
872		for (; x < end; ++x) {
873			softwareRenderer->row[x] = backdrop;
874		}
875	}
876
877	if (softwareRenderer->blendDirty) {
878		_updatePalettes(softwareRenderer);
879		softwareRenderer->blendDirty = false;
880	}
881}
882
883void GBAVideoSoftwareRendererPostprocessBuffer(struct GBAVideoSoftwareRenderer* softwareRenderer) {
884	int x, w;
885	if (softwareRenderer->target2Bd) {
886		x = 0;
887		for (w = 0; w < softwareRenderer->nWindows; ++w) {
888			uint32_t backdrop = 0;
889			if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
890				backdrop |= softwareRenderer->normalPalette[0];
891			} else {
892				backdrop |= softwareRenderer->variantPalette[0];
893			}
894			int end = softwareRenderer->windows[w].endX;
895			for (; x < end; ++x) {
896				uint32_t color = softwareRenderer->row[x];
897				if (color & FLAG_TARGET_1) {
898					softwareRenderer->row[x] = _mix(softwareRenderer->alphaB[x], backdrop, softwareRenderer->alphaA[x], color);
899				}
900			}
901		}
902	}
903	if (softwareRenderer->target1Obj && (softwareRenderer->blendEffect == BLEND_DARKEN || softwareRenderer->blendEffect == BLEND_BRIGHTEN)) {
904		x = 0;
905		for (w = 0; w < softwareRenderer->nWindows; ++w) {
906			if (!GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
907				continue;
908			}
909			int end = softwareRenderer->windows[w].endX;
910			if (softwareRenderer->blendEffect == BLEND_DARKEN) {
911				for (; x < end; ++x) {
912					uint32_t color = softwareRenderer->row[x];
913					if ((color & 0xFF000000) == FLAG_REBLEND) {
914						softwareRenderer->row[x] = _darken(color, softwareRenderer->bldy);
915					}
916				}
917			} else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
918				for (; x < end; ++x) {
919					uint32_t color = softwareRenderer->row[x];
920					if ((color & 0xFF000000) == FLAG_REBLEND) {
921						softwareRenderer->row[x] = _brighten(color, softwareRenderer->bldy);
922					}
923				}
924			}
925		}
926	}
927}
928
929int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRenderer* renderer, int y) {
930	int w;
931	int spriteLayers = 0;
932	if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt) && !renderer->d.disableOBJ) {
933		if (renderer->oamDirty) {
934			renderer->oamMax = GBAVideoRendererCleanOAM(renderer->d.oam->obj, renderer->sprites, renderer->objOffsetY, renderer->masterHeight, renderer->combinedObjSort);
935			renderer->oamDirty = false;
936		}
937		renderer->spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(renderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH;
938		int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
939		int mosaicY = y - (y % mosaicV);
940		int i;
941		for (i = 0; i < renderer->oamMax; ++i) {
942			struct GBAVideoRendererSprite* sprite = &renderer->sprites[i];
943			int localY = y;
944			renderer->end = 0;
945			if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) {
946				continue;
947			}
948			if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
949				localY = mosaicY;
950				if (localY < sprite->y) {
951					localY = sprite->y;
952				}
953				if (localY >= sprite->endY) {
954					localY = sprite->endY - 1;
955				}
956			}
957			for (w = 0; w < renderer->nWindows; ++w) {
958				if (renderer->spriteCyclesRemaining <= 0) {
959					break;
960				}
961				renderer->currentWindow = renderer->windows[w].control;
962				renderer->start = renderer->end;
963				renderer->end = renderer->windows[w].endX;
964				if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
965					continue;
966				}
967
968				int drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY);
969				spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
970			}
971			if (renderer->spriteCyclesRemaining <= 0) {
972				break;
973			}
974		}
975	}
976	return spriteLayers;
977}
978
979static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
980	int i;
981	memset(renderer->alphaA, renderer->blda, sizeof(renderer->alphaA));
982	memset(renderer->alphaB, renderer->bldb, sizeof(renderer->alphaB));
983	if (renderer->blendEffect == BLEND_BRIGHTEN) {
984		for (i = 0; i < 512; ++i) {
985			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
986		}
987	} else if (renderer->blendEffect == BLEND_DARKEN) {
988		for (i = 0; i < 512; ++i) {
989			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
990		}
991	} else {
992		for (i = 0; i < 512; ++i) {
993			renderer->variantPalette[i] = renderer->normalPalette[i];
994		}
995	}
996}