all repos — mgba @ fe2f67e2aa2246e8ec2c2599a480d6ff7557f3f7

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