all repos — mgba @ ef742cb1282b13ae4a1df471c38e1445424e60e8

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