all repos — mgba @ 1c6d87f578edee720b4e9263403b39c7182917ed

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