all repos — mgba @ 9639d7ad49ee24ca045c6db170bee825d615538b

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/tile-cache.h>
  9#include <mgba/internal/arm/macros.h>
 10#include <mgba/internal/gba/io.h>
 11
 12#include <mgba-util/arm-algo.h>
 13#include <mgba-util/memory.h>
 14
 15static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
 16static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
 17static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer);
 18static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
 19static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 20static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 21static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 22static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 23static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
 24static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
 25static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
 26
 27static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
 28static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
 29static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
 30static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
 31static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
 32static void GBAVideoSoftwareRendererWriteBGPD(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 _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
 40static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
 41
 42static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
 43
 44static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y);
 45static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win);
 46
 47void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
 48	renderer->d.init = GBAVideoSoftwareRendererInit;
 49	renderer->d.reset = GBAVideoSoftwareRendererReset;
 50	renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
 51	renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
 52	renderer->d.writeVRAM = GBAVideoSoftwareRendererWriteVRAM;
 53	renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
 54	renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
 55	renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
 56	renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
 57	renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels;
 58	renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels;
 59
 60	renderer->d.disableBG[0] = false;
 61	renderer->d.disableBG[1] = false;
 62	renderer->d.disableBG[2] = false;
 63	renderer->d.disableBG[3] = false;
 64	renderer->d.disableOBJ = false;
 65	renderer->tileStride = 0x20;
 66	renderer->masterEnd = VIDEO_HORIZONTAL_PIXELS;
 67
 68	renderer->temporaryBuffer = 0;
 69}
 70
 71static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
 72	GBAVideoSoftwareRendererReset(renderer);
 73
 74	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 75
 76	int y;
 77	for (y = 0; y < softwareRenderer->masterEnd; ++y) {
 78		color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
 79		int x;
 80		for (x = 0; x < softwareRenderer->masterEnd; ++x) {
 81			row[x] = GBA_COLOR_WHITE;
 82		}
 83	}
 84}
 85
 86static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
 87	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 88	int i;
 89
 90	softwareRenderer->dispcnt = 0x0080;
 91
 92	softwareRenderer->target1Obj = 0;
 93	softwareRenderer->target1Bd = 0;
 94	softwareRenderer->target2Obj = 0;
 95	softwareRenderer->target2Bd = 0;
 96	softwareRenderer->blendEffect = BLEND_NONE;
 97	for (i = 0; i < 1024; i += 2) {
 98		uint16_t entry;
 99		LOAD_16(entry, i, softwareRenderer->d.palette);
100		GBAVideoSoftwareRendererWritePalette(renderer, i, entry);
101	}
102	_updatePalettes(softwareRenderer);
103
104	softwareRenderer->blda = 0;
105	softwareRenderer->bldb = 0;
106	softwareRenderer->bldy = 0;
107
108	softwareRenderer->winN[0] = (struct WindowN) { .control = { .priority = 0 } };
109	softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } };
110	softwareRenderer->objwin = (struct WindowControl) { .priority = 2 };
111	softwareRenderer->winout = (struct WindowControl) { .priority = 3 };
112	softwareRenderer->oamMax = 0;
113
114	softwareRenderer->mosaic = 0;
115
116	for (i = 0; i < 4; ++i) {
117		struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
118		bg->index = i;
119		bg->enabled = 0;
120		bg->priority = 0;
121		bg->charBase = 0;
122		bg->mosaic = 0;
123		bg->multipalette = 0;
124		bg->screenBase = 0;
125		bg->overflow = 0;
126		bg->size = 0;
127		bg->target1 = 0;
128		bg->target2 = 0;
129		bg->x = 0;
130		bg->y = 0;
131		bg->refx = 0;
132		bg->refy = 0;
133		bg->dx = 256;
134		bg->dmx = 0;
135		bg->dy = 0;
136		bg->dmy = 256;
137		bg->sx = 0;
138		bg->sy = 0;
139	}
140}
141
142static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
143	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
144	UNUSED(softwareRenderer);
145}
146
147static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
148	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
149	switch (address) {
150	case REG_DISPCNT:
151		softwareRenderer->dispcnt = value;
152		GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
153		break;
154	case REG_BG0CNT:
155		value &= 0xDFFF;
156		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
157		break;
158	case REG_BG1CNT:
159		value &= 0xDFFF;
160		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
161		break;
162	case REG_BG2CNT:
163		value &= 0xFFFF;
164		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
165		break;
166	case REG_BG3CNT:
167		value &= 0xFFFF;
168		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
169		break;
170	case REG_BG0HOFS:
171		value &= 0x01FF;
172		softwareRenderer->bg[0].x = value;
173		break;
174	case REG_BG0VOFS:
175		value &= 0x01FF;
176		softwareRenderer->bg[0].y = value;
177		break;
178	case REG_BG1HOFS:
179		value &= 0x01FF;
180		softwareRenderer->bg[1].x = value;
181		break;
182	case REG_BG1VOFS:
183		value &= 0x01FF;
184		softwareRenderer->bg[1].y = value;
185		break;
186	case REG_BG2HOFS:
187		value &= 0x01FF;
188		softwareRenderer->bg[2].x = value;
189		break;
190	case REG_BG2VOFS:
191		value &= 0x01FF;
192		softwareRenderer->bg[2].y = value;
193		break;
194	case REG_BG3HOFS:
195		value &= 0x01FF;
196		softwareRenderer->bg[3].x = value;
197		break;
198	case REG_BG3VOFS:
199		value &= 0x01FF;
200		softwareRenderer->bg[3].y = value;
201		break;
202	case REG_BG2PA:
203		GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
204		break;
205	case REG_BG2PB:
206		GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
207		break;
208	case REG_BG2PC:
209		GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
210		break;
211	case REG_BG2PD:
212		GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
213		break;
214	case REG_BG2X_LO:
215		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
216		break;
217	case REG_BG2X_HI:
218		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
219		break;
220	case REG_BG2Y_LO:
221		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
222		break;
223	case REG_BG2Y_HI:
224		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
225		break;
226	case REG_BG3PA:
227		GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
228		break;
229	case REG_BG3PB:
230		GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
231		break;
232	case REG_BG3PC:
233		GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
234		break;
235	case REG_BG3PD:
236		GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
237		break;
238	case REG_BG3X_LO:
239		GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
240		break;
241	case REG_BG3X_HI:
242		GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
243		break;
244	case REG_BG3Y_LO:
245		GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
246		break;
247	case REG_BG3Y_HI:
248		GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
249		break;
250	case REG_BLDCNT:
251		GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
252		value &= 0x3FFF;
253		break;
254	case REG_BLDALPHA:
255		softwareRenderer->blda = value & 0x1F;
256		if (softwareRenderer->blda > 0x10) {
257			softwareRenderer->blda = 0x10;
258		}
259		softwareRenderer->bldb = (value >> 8) & 0x1F;
260		if (softwareRenderer->bldb > 0x10) {
261			softwareRenderer->bldb = 0x10;
262		}
263		value &= 0x1F1F;
264		break;
265	case REG_BLDY:
266		softwareRenderer->bldy = value & 0x1F;
267		if (softwareRenderer->bldy > 0x10) {
268			softwareRenderer->bldy = 0x10;
269		}
270		_updatePalettes(softwareRenderer);
271		break;
272	case REG_WIN0H:
273		softwareRenderer->winN[0].h.end = value;
274		softwareRenderer->winN[0].h.start = value >> 8;
275		if (softwareRenderer->winN[0].h.start > softwareRenderer->masterEnd && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
276			softwareRenderer->winN[0].h.start = 0;
277		}
278		if (softwareRenderer->winN[0].h.end > softwareRenderer->masterEnd) {
279			softwareRenderer->winN[0].h.end = softwareRenderer->masterEnd;
280			if (softwareRenderer->winN[0].h.start > softwareRenderer->masterEnd) {
281				softwareRenderer->winN[0].h.start = softwareRenderer->masterEnd;
282			}
283		}
284		break;
285	case REG_WIN1H:
286		softwareRenderer->winN[1].h.end = value;
287		softwareRenderer->winN[1].h.start = value >> 8;
288		if (softwareRenderer->winN[1].h.start > softwareRenderer->masterEnd && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
289			softwareRenderer->winN[1].h.start = 0;
290		}
291		if (softwareRenderer->winN[1].h.end > softwareRenderer->masterEnd) {
292			softwareRenderer->winN[1].h.end = softwareRenderer->masterEnd;
293			if (softwareRenderer->winN[1].h.start > softwareRenderer->masterEnd) {
294				softwareRenderer->winN[1].h.start = softwareRenderer->masterEnd;
295			}
296		}
297		break;
298	case REG_WIN0V:
299		softwareRenderer->winN[0].v.end = value;
300		softwareRenderer->winN[0].v.start = value >> 8;
301		if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
302			softwareRenderer->winN[0].v.start = 0;
303		}
304		if (softwareRenderer->winN[0].v.end > VIDEO_VERTICAL_PIXELS) {
305			softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
306			if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS) {
307				softwareRenderer->winN[0].v.start = VIDEO_VERTICAL_PIXELS;
308			}
309		}
310		break;
311	case REG_WIN1V:
312		softwareRenderer->winN[1].v.end = value;
313		softwareRenderer->winN[1].v.start = value >> 8;
314		if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
315			softwareRenderer->winN[1].v.start = 0;
316		}
317		if (softwareRenderer->winN[1].v.end > VIDEO_VERTICAL_PIXELS) {
318			softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
319			if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS) {
320				softwareRenderer->winN[1].v.start = VIDEO_VERTICAL_PIXELS;
321			}
322		}
323		break;
324	case REG_WININ:
325		value &= 0x3F3F;
326		softwareRenderer->winN[0].control.packed = value;
327		softwareRenderer->winN[1].control.packed = value >> 8;
328		break;
329	case REG_WINOUT:
330		value &= 0x3F3F;
331		softwareRenderer->winout.packed = value;
332		softwareRenderer->objwin.packed = value >> 8;
333		break;
334	case REG_MOSAIC:
335		softwareRenderer->mosaic = value;
336		break;
337	case REG_GREENSWP:
338		mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);
339		break;
340	default:
341		mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
342	}
343	return value;
344}
345
346static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
347	if (renderer->cache) {
348		mTileCacheWriteVRAM(renderer->cache, address);
349	}
350}
351
352static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
353	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
354	softwareRenderer->oamDirty = 1;
355	UNUSED(oam);
356}
357
358static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
359	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
360#ifdef COLOR_16_BIT
361#ifdef COLOR_5_6_5
362	unsigned color = 0;
363	color |= (value & 0x001F) << 11;
364	color |= (value & 0x03E0) << 1;
365	color |= (value & 0x7C00) >> 10;
366#else
367	unsigned color = value;
368#endif
369#else
370	unsigned color = 0;
371	color |= (value << 3) & 0xF8;
372	color |= (value << 6) & 0xF800;
373	color |= (value << 9) & 0xF80000;
374	color |= (color >> 5) & 0x070707;
375#endif
376	softwareRenderer->normalPalette[address >> 1] = color;
377	if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
378		softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
379	} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
380		softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
381	}
382	if (renderer->cache) {
383		mTileCacheWritePalette(renderer->cache, address);
384	}
385}
386
387static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y) {
388	if (win->v.end >= win->v.start) {
389		if (y >= win->v.end) {
390			return;
391		}
392		if (y < win->v.start) {
393			return;
394		}
395	} else if (y >= win->v.end && y < win->v.start) {
396		return;
397	}
398	if (win->h.end > softwareRenderer->masterEnd || win->h.end < win->h.start) {
399		struct WindowN splits[2] = { *win, *win };
400		splits[0].h.start = 0;
401		splits[1].h.end = softwareRenderer->masterEnd;
402		_breakWindowInner(softwareRenderer, &splits[0]);
403		_breakWindowInner(softwareRenderer, &splits[1]);
404	} else {
405		_breakWindowInner(softwareRenderer, win);
406	}
407}
408
409static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
410	int activeWindow;
411	int startX = 0;
412	if (win->h.end > 0) {
413		for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
414			if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
415				// Insert a window before the end of the active window
416				struct Window oldWindow = softwareRenderer->windows[activeWindow];
417				if (win->h.start > startX) {
418					// And after the start of the active window
419					int nextWindow = softwareRenderer->nWindows;
420					++softwareRenderer->nWindows;
421					for (; nextWindow > activeWindow; --nextWindow) {
422						softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
423					}
424					softwareRenderer->windows[activeWindow].endX = win->h.start;
425					++activeWindow;
426				}
427				softwareRenderer->windows[activeWindow].control = win->control;
428				softwareRenderer->windows[activeWindow].endX = win->h.end;
429				if (win->h.end >= oldWindow.endX) {
430					// Trim off extra windows we've overwritten
431					for (++activeWindow; softwareRenderer->nWindows > activeWindow + 1 && win->h.end >= softwareRenderer->windows[activeWindow].endX; ++activeWindow) {
432						if (VIDEO_CHECKS && activeWindow >= MAX_WINDOW) {
433							mLOG(GBA_VIDEO, FATAL, "Out of bounds window write will occur");
434							return;
435						}
436						softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
437						--softwareRenderer->nWindows;
438					}
439				} else {
440					++activeWindow;
441					int nextWindow = softwareRenderer->nWindows;
442					++softwareRenderer->nWindows;
443					for (; nextWindow > activeWindow; --nextWindow) {
444						softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
445					}
446					softwareRenderer->windows[activeWindow] = oldWindow;
447				}
448				break;
449			}
450			startX = softwareRenderer->windows[activeWindow].endX;
451		}
452	}
453#ifdef DEBUG
454	if (softwareRenderer->nWindows > MAX_WINDOW) {
455		mLOG(GBA_VIDEO, FATAL, "Out of bounds window write occurred!");
456	}
457#endif
458}
459
460static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
461	int i;
462	int oamMax = 0;
463	for (i = 0; i < 128; ++i) {
464		struct GBAObj obj;
465		LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a);
466		LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b);
467		LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c);
468		if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
469			int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(obj.a) * 4 + GBAObjAttributesBGetSize(obj.b)][1];
470			if (GBAObjAttributesAIsTransformed(obj.a)) {
471				height <<= GBAObjAttributesAGetDoubleSize(obj.a);
472			}
473			if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
474				renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a);
475				renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height;
476				renderer->sprites[oamMax].obj = obj;
477				++oamMax;
478			}
479		}
480	}
481	renderer->oamMax = oamMax;
482	renderer->oamDirty = 0;
483}
484
485static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
486	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
487
488	color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
489	if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
490		int x;
491		for (x = 0; x < softwareRenderer->masterEnd; ++x) {
492			row[x] = GBA_COLOR_WHITE;
493		}
494		return;
495	}
496
497	int x;
498	for (x = 0; x < softwareRenderer->masterEnd; x += 4) {
499		softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
500		softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
501		softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
502		softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
503	}
504
505	softwareRenderer->windows[0].endX = softwareRenderer->masterEnd;
506	softwareRenderer->nWindows = 1;
507	if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
508		softwareRenderer->windows[0].control = softwareRenderer->winout;
509		if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt)) {
510			_breakWindow(softwareRenderer, &softwareRenderer->winN[1], y);
511		}
512		if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt)) {
513			_breakWindow(softwareRenderer, &softwareRenderer->winN[0], y);
514		}
515	} else {
516		softwareRenderer->windows[0].control.packed = 0xFF;
517	}
518
519	GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
520
521	int w;
522	x = 0;
523	for (w = 0; w < softwareRenderer->nWindows; ++w) {
524		// TOOD: handle objwin on backdrop
525		uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
526		if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
527			backdrop |= softwareRenderer->normalPalette[0];
528		} else {
529			backdrop |= softwareRenderer->variantPalette[0];
530		}
531		int end = softwareRenderer->windows[w].endX;
532		for (; x < end - 3; x += 4) {
533			softwareRenderer->row[x] = backdrop;
534			softwareRenderer->row[x + 1] = backdrop;
535			softwareRenderer->row[x + 2] = backdrop;
536			softwareRenderer->row[x + 3] = backdrop;
537		}
538		for (; x < end; ++x) {
539			softwareRenderer->row[x] = backdrop;
540		}
541	}
542
543	_drawScanline(softwareRenderer, y);
544
545	if (softwareRenderer->target2Bd) {
546		x = 0;
547		for (w = 0; w < softwareRenderer->nWindows; ++w) {
548			uint32_t backdrop = 0;
549			if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
550				backdrop |= softwareRenderer->normalPalette[0];
551			} else {
552				backdrop |= softwareRenderer->variantPalette[0];
553			}
554			int end = softwareRenderer->windows[w].endX;
555			for (; x < end; ++x) {
556				uint32_t color = softwareRenderer->row[x];
557				if (color & FLAG_TARGET_1) {
558					softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
559				}
560			}
561		}
562	}
563	if (softwareRenderer->target1Obj && (softwareRenderer->blendEffect == BLEND_DARKEN || softwareRenderer->blendEffect == BLEND_BRIGHTEN)) {
564		x = 0;
565		for (w = 0; w < softwareRenderer->nWindows; ++w) {
566			if (!GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
567				continue;
568			}
569			int end = softwareRenderer->windows[w].endX;
570			if (softwareRenderer->blendEffect == BLEND_DARKEN) {
571				for (; x < end; ++x) {
572					uint32_t color = softwareRenderer->row[x];
573					if ((color & 0xFF000000) == FLAG_REBLEND) {
574						softwareRenderer->row[x] = _darken(color, softwareRenderer->bldy);
575					}
576				}
577			} else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
578				for (; x < end; ++x) {
579					uint32_t color = softwareRenderer->row[x];
580					if ((color & 0xFF000000) == FLAG_REBLEND) {
581						softwareRenderer->row[x] = _brighten(color, softwareRenderer->bldy);
582					}
583				}
584			}
585		}
586	}
587
588#ifdef COLOR_16_BIT
589#if defined(__ARM_NEON) && !defined(__APPLE__)
590	_to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
591#else
592	for (x = 0; x < softwareRenderer->masterEnd; ++x) {
593		row[x] = softwareRenderer->row[x];
594	}
595#endif
596#else
597	memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
598#endif
599}
600
601static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
602	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
603
604	if (softwareRenderer->temporaryBuffer) {
605		mappedMemoryFree(softwareRenderer->temporaryBuffer, softwareRenderer->masterEnd * VIDEO_VERTICAL_PIXELS * 4);
606		softwareRenderer->temporaryBuffer = 0;
607	}
608	softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
609	softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
610	softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
611	softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
612}
613
614static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
615	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
616	*stride = softwareRenderer->outputBufferStride;
617	*pixels = softwareRenderer->outputBuffer;
618}
619
620static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
621	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
622
623	const color_t* colorPixels = pixels;
624	unsigned i;
625	for (i = 0; i < VIDEO_VERTICAL_PIXELS; ++i) {
626		memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], softwareRenderer->masterEnd * BYTES_PER_PIXEL);
627	}
628}
629
630static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
631	renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt) && !renderer->d.disableBG[0];
632	renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt) && !renderer->d.disableBG[1];
633	renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt) && !renderer->d.disableBG[2];
634	renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt) && !renderer->d.disableBG[3];
635}
636
637static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
638	UNUSED(renderer);
639	bg->priority = GBARegisterBGCNTGetPriority(value);
640	bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14;
641	if (!renderer->d.vramBG[4]) {
642		bg->charBase &= 0xC000;
643	}
644	bg->mosaic = GBARegisterBGCNTGetMosaic(value);
645	bg->multipalette = GBARegisterBGCNTGet256Color(value);
646	bg->screenBase &= ~0xF800;
647	bg->screenBase |= GBARegisterBGCNTGetScreenBase(value) << 11;
648	bg->overflow = GBARegisterBGCNTGetOverflow(value);
649	bg->size = GBARegisterBGCNTGetSize(value);
650	bg->control = value;
651}
652
653static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
654	bg->dx = value;
655}
656
657static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
658	bg->dmx = value;
659}
660
661static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
662	bg->dy = value;
663}
664
665static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
666	bg->dmy = value;
667}
668
669static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
670	bg->refx = (bg->refx & 0xFFFF0000) | value;
671	bg->sx = bg->refx;
672}
673
674static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
675	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
676	bg->refx <<= 4;
677	bg->refx >>= 4;
678	bg->sx = bg->refx;
679}
680
681static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
682	bg->refy = (bg->refy & 0xFFFF0000) | value;
683	bg->sy = bg->refy;
684}
685
686static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
687	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
688	bg->refy <<= 4;
689	bg->refy >>= 4;
690	bg->sy = bg->refy;
691}
692
693static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
694	enum BlendEffect oldEffect = renderer->blendEffect;
695
696	renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
697	renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
698	renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
699	renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
700	renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
701	renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
702	renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
703	renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
704
705	renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
706	renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
707	renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
708	renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
709	renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
710
711	if (oldEffect != renderer->blendEffect) {
712		_updatePalettes(renderer);
713	}
714}
715
716#define TEST_LAYER_ENABLED(X) \
717	(renderer->bg[X].enabled && \
718	(GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \
719	(GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \
720	renderer->bg[X].priority == priority)
721
722static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
723	int w;
724	renderer->end = 0;
725	int spriteLayers = 0;
726	if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt) && !renderer->d.disableOBJ) {
727		if (renderer->oamDirty) {
728			_cleanOAM(renderer);
729		}
730		renderer->spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(renderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH;
731		int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
732		int mosaicY = y - (y % mosaicV);
733		for (w = 0; w < renderer->nWindows; ++w) {
734			renderer->start = renderer->end;
735			renderer->end = renderer->windows[w].endX;
736			renderer->currentWindow = renderer->windows[w].control;
737			if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
738				continue;
739			}
740			int i;
741			int drawn;
742
743			for (i = 0; i < renderer->oamMax; ++i) {
744				int localY = y;
745				if (renderer->spriteCyclesRemaining <= 0) {
746					break;
747				}
748				struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
749				if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
750					localY = mosaicY;
751				}
752				if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
753					continue;
754				}
755				drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY);
756				spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
757			}
758		}
759	}
760
761	unsigned priority;
762	for (priority = 0; priority < 4; ++priority) {
763		renderer->end = 0;
764		for (w = 0; w < renderer->nWindows; ++w) {
765			renderer->start = renderer->end;
766			renderer->end = renderer->windows[w].endX;
767			renderer->currentWindow = renderer->windows[w].control;
768			if (spriteLayers & (1 << priority)) {
769				GBAVideoSoftwareRendererPostprocessSprite(renderer, priority);
770			}
771			if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
772				GBAVideoSoftwareRendererDrawBackgroundMode0(renderer, &renderer->bg[0], y);
773			}
774			if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
775				GBAVideoSoftwareRendererDrawBackgroundMode0(renderer, &renderer->bg[1], y);
776			}
777			if (TEST_LAYER_ENABLED(2)) {
778				switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
779				case 0:
780					GBAVideoSoftwareRendererDrawBackgroundMode0(renderer, &renderer->bg[2], y);
781					break;
782				case 1:
783				case 2:
784					GBAVideoSoftwareRendererDrawBackgroundMode2(renderer, &renderer->bg[2], y);
785					break;
786				case 3:
787					GBAVideoSoftwareRendererDrawBackgroundMode3(renderer, &renderer->bg[2], y);
788					break;
789				case 4:
790					GBAVideoSoftwareRendererDrawBackgroundMode4(renderer, &renderer->bg[2], y);
791					break;
792				case 5:
793					GBAVideoSoftwareRendererDrawBackgroundMode5(renderer, &renderer->bg[2], y);
794					break;
795				}
796			}
797			if (TEST_LAYER_ENABLED(3)) {
798				switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
799				case 0:
800					GBAVideoSoftwareRendererDrawBackgroundMode0(renderer, &renderer->bg[3], y);
801					break;
802				case 2:
803					GBAVideoSoftwareRendererDrawBackgroundMode2(renderer, &renderer->bg[3], y);
804					break;
805				}
806			}
807		}
808	}
809	renderer->bg[2].sx += renderer->bg[2].dmx;
810	renderer->bg[2].sy += renderer->bg[2].dmy;
811	renderer->bg[3].sx += renderer->bg[3].dmx;
812	renderer->bg[3].sy += renderer->bg[3].dmy;
813}
814
815static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
816	int i;
817	if (renderer->blendEffect == BLEND_BRIGHTEN) {
818		for (i = 0; i < 512; ++i) {
819			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
820		}
821	} else if (renderer->blendEffect == BLEND_DARKEN) {
822		for (i = 0; i < 512; ++i) {
823			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
824		}
825	} else {
826		for (i = 0; i < 512; ++i) {
827			renderer->variantPalette[i] = renderer->normalPalette[i];
828		}
829	}
830}