all repos — mgba @ 9b5bda237a75d6d82fafe5a8406811bd6b017b9d

mGBA Game Boy Advance Emulator

src/ds/renderers/software.c (view raw)

  1/* Copyright (c) 2013-2017 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 <mgba/internal/ds/renderers/software.h>
  7#include "gba/renderers/software-private.h"
  8
  9#include <mgba/internal/arm/macros.h>
 10#include <mgba/internal/ds/gx.h>
 11#include <mgba/internal/ds/io.h>
 12
 13static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer);
 14static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer);
 15static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
 16static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
 17static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
 18static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
 19static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
 20static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
 21static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
 22static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
 23static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
 24
 25static void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
 26
 27static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool obj, bool engB, int slot) {
 28	color_t* palette;
 29	color_t* variantPalette;
 30	struct GBAVideoSoftwareRenderer* softwareRenderer;
 31	uint16_t* vram;
 32	if (!obj) {
 33		if (!engB) {
 34			palette = &renderer->extPaletteA[slot * 4096];
 35			variantPalette = &renderer->variantPaletteA[slot * 4096];
 36			softwareRenderer = &renderer->engA;
 37			vram = renderer->d.vramABGExtPal[slot];
 38		} else {
 39			palette = &renderer->extPaletteB[slot * 4096];
 40			variantPalette = &renderer->variantPaletteB[slot * 4096];
 41			softwareRenderer = &renderer->engB;
 42			vram = renderer->d.vramBBGExtPal[slot];
 43		}
 44	} else {
 45		if (!engB) {
 46			palette = renderer->objExtPaletteA;
 47			variantPalette = renderer->variantPaletteA;
 48			softwareRenderer = &renderer->engA;
 49			vram = renderer->d.vramAOBJExtPal;
 50		} else {
 51			palette = renderer->objExtPaletteB;
 52			variantPalette = renderer->variantPaletteB;
 53			softwareRenderer = &renderer->engB;
 54			vram = renderer->d.vramBOBJExtPal;
 55		}
 56	}
 57	if (!vram) {
 58		return false;
 59	}
 60	int i;
 61	for (i = 0; i < 4096; ++i) {
 62		uint16_t value = vram[i];
 63#ifdef COLOR_16_BIT
 64#ifdef COLOR_5_6_5
 65		unsigned color = 0;
 66		color |= (value & 0x001F) << 11;
 67		color |= (value & 0x03E0) << 1;
 68		color |= (value & 0x7C00) >> 10;
 69#else
 70		unsigned color = value;
 71#endif
 72#else
 73		unsigned color = 0;
 74		color |= (value << 3) & 0xF8;
 75		color |= (value << 6) & 0xF800;
 76		color |= (value << 9) & 0xF80000;
 77		color |= (color >> 5) & 0x070707;
 78#endif
 79		palette[i] = color;
 80		if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
 81			variantPalette[i] = _brighten(color, softwareRenderer->bldy);
 82		} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
 83			variantPalette[i] = _darken(color, softwareRenderer->bldy);
 84		}
 85	}
 86	if (obj) {
 87		softwareRenderer->objExtPalette = palette;
 88	} else {
 89		if (slot >= 2) {
 90			if (GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot - 2].control)) {
 91				softwareRenderer->bg[slot - 2].extPalette = palette;
 92				softwareRenderer->bg[slot - 2].variantPalette = variantPalette;
 93			}
 94		} else if (slot < 2 && !GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot].control) ) {
 95			softwareRenderer->bg[slot].extPalette = palette;
 96			softwareRenderer->bg[slot].variantPalette = variantPalette;
 97		}
 98		softwareRenderer->bg[slot].extPalette = palette;
 99		softwareRenderer->bg[slot].variantPalette = variantPalette;
100	}
101	return true;
102}
103
104static void _updateCharBase(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
105	struct GBAVideoSoftwareRenderer* eng;
106	if (!engB) {
107		eng = &softwareRenderer->engA;
108	} else {
109		eng = &softwareRenderer->engB;
110	}
111	int i;
112	uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
113	uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
114	for (i = 0; i < 4; ++i) {
115		if (!engB) {
116			uint32_t control = eng->bg[i].control;
117			eng->d.writeVideoRegister(&eng->d, DS9_REG_A_BG0CNT + i * 2, control);
118			eng->bg[i].control = control;
119		}
120
121		eng->bg[i].charBase = GBARegisterBGCNTGetCharBase(eng->bg[i].control) << 14;
122
123		if (!engB) {
124			softwareRenderer->engA.bg[i].charBase += charBase;
125			softwareRenderer->engA.bg[i].screenBase &= ~0x70000;
126			softwareRenderer->engA.bg[i].screenBase |= screenBase;
127		}
128	}
129}
130
131
132void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
133	renderer->d.init = DSVideoSoftwareRendererInit;
134	renderer->d.reset = DSVideoSoftwareRendererReset;
135	renderer->d.deinit = DSVideoSoftwareRendererDeinit;
136	renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
137	renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
138	renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
139	renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
140	renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
141	renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
142	renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
143	renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
144
145	renderer->engA.d.cache = NULL;
146	GBAVideoSoftwareRendererCreate(&renderer->engA);
147	renderer->engB.d.cache = NULL;
148	GBAVideoSoftwareRendererCreate(&renderer->engB);
149}
150
151static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
152	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
153	softwareRenderer->engA.d.palette = &renderer->palette[0];
154	softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
155	softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
156	softwareRenderer->engA.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
157	softwareRenderer->engA.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
158	softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
159	softwareRenderer->engB.d.palette = &renderer->palette[512];
160	softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
161	softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
162	softwareRenderer->engB.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
163	softwareRenderer->engB.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
164	softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
165
166	DSVideoSoftwareRendererReset(renderer);
167}
168
169static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
170	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
171	softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
172	softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
173	softwareRenderer->powcnt = 0;
174	softwareRenderer->dispcntA = 0;
175	softwareRenderer->dispcntB = 0;
176}
177
178static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
179	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
180	softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
181	softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
182}
183
184static void DSVideoSoftwareRendererUpdateDISPCNT(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
185	uint32_t dispcnt;
186	struct GBAVideoSoftwareRenderer* eng;
187	if (!engB) {
188		dispcnt = softwareRenderer->dispcntA;
189		eng = &softwareRenderer->engA;
190	} else {
191		dispcnt = softwareRenderer->dispcntB;
192		eng = &softwareRenderer->engB;
193	}
194	uint16_t fakeDispcnt = dispcnt & 0xFF87;
195	if (!DSRegisterDISPCNTIsTileObjMapping(dispcnt)) {
196		eng->tileStride = 0x20;
197	} else {
198		eng->tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(dispcnt);
199		fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
200	}
201	eng->d.writeVideoRegister(&eng->d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
202	eng->dispcnt |= dispcnt & 0xFFFF0000;
203	if (DSRegisterDISPCNTIsBgExtPalette(dispcnt)) {
204		color_t* extPalette;
205		if (!engB) {
206			extPalette = softwareRenderer->extPaletteA;
207		} else {
208			extPalette = softwareRenderer->extPaletteB;
209		}
210		int i;
211		for (i = 0; i < 4; ++i) {
212			int slot = i;
213			if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(eng->bg[i].control)) {
214				slot += 2;
215			}
216			if (eng->bg[i].extPalette != &extPalette[slot * 4096]) {
217				_regenerateExtPalette(softwareRenderer, false, engB, slot);
218			}
219		}
220	} else {
221		eng->bg[0].extPalette = NULL;
222		eng->bg[1].extPalette = NULL;
223		eng->bg[2].extPalette = NULL;
224		eng->bg[3].extPalette = NULL;
225	}
226	if (DSRegisterDISPCNTIsObjExtPalette(dispcnt)) {
227		if (!engB) {
228			if (softwareRenderer->engA.objExtPalette != softwareRenderer->objExtPaletteA) {
229				_regenerateExtPalette(softwareRenderer, true, engB, 0);
230			}
231		} else {
232			if (softwareRenderer->engB.objExtPalette != softwareRenderer->objExtPaletteB) {
233				_regenerateExtPalette(softwareRenderer, true, engB, 0);
234			}
235		}
236	} else {
237		if (!engB) {
238			softwareRenderer->engA.objExtPalette = NULL;
239		} else {
240			softwareRenderer->engB.objExtPalette = NULL;
241		}
242	}
243	if (!engB) {
244		eng->dispcnt = DSRegisterDISPCNTClear3D(eng->dispcnt);
245		eng->dispcnt |= DSRegisterDISPCNTIs3D(dispcnt);
246		_updateCharBase(softwareRenderer, engB);
247	}
248}
249
250static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
251	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
252	if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
253		softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
254	} else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
255		softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
256	} else {
257		mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
258	}
259	switch (address) {
260	case DS9_REG_A_BG0CNT:
261	case DS9_REG_A_BG1CNT:
262		softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
263		// Fall through
264	case DS9_REG_A_BG2CNT:
265	case DS9_REG_A_BG3CNT:
266		_updateCharBase(softwareRenderer, false);
267		break;
268	case DS9_REG_B_BG0CNT:
269	case DS9_REG_B_BG1CNT:
270		softwareRenderer->engB.bg[(address - DS9_REG_B_BG0CNT) >> 1].control = value;
271		// Fall through
272	case DS9_REG_B_BG2CNT:
273	case DS9_REG_B_BG3CNT:
274		_updateCharBase(softwareRenderer, true);
275		break;
276	case DS9_REG_A_MASTER_BRIGHT:
277		softwareRenderer->engA.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
278		softwareRenderer->engA.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
279		if (softwareRenderer->engA.masterBrightY > 0x10) {
280			softwareRenderer->engA.masterBrightY = 0x10;
281		}
282		break;
283	case DS9_REG_B_MASTER_BRIGHT:
284		softwareRenderer->engB.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
285		softwareRenderer->engB.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
286		if (softwareRenderer->engB.masterBrightY > 0x10) {
287			softwareRenderer->engB.masterBrightY = 0x10;
288		}
289		break;
290	case DS9_REG_A_BLDCNT:
291	case DS9_REG_A_BLDY:
292		// TODO: Optimize
293		_regenerateExtPalette(softwareRenderer, false, false, 0);
294		_regenerateExtPalette(softwareRenderer, false, false, 1);
295		_regenerateExtPalette(softwareRenderer, false, false, 2);
296		_regenerateExtPalette(softwareRenderer, false, false, 3);
297		_regenerateExtPalette(softwareRenderer, true, false, 0);
298		break;
299	case DS9_REG_B_BLDCNT:
300	case DS9_REG_B_BLDY:
301		// TODO: Optimize
302		_regenerateExtPalette(softwareRenderer, false, true, 0);
303		_regenerateExtPalette(softwareRenderer, false, true, 1);
304		_regenerateExtPalette(softwareRenderer, false, true, 2);
305		_regenerateExtPalette(softwareRenderer, false, true, 3);
306		_regenerateExtPalette(softwareRenderer, true, true, 0);
307		break;
308	case DS9_REG_A_DISPCNT_LO:
309		softwareRenderer->dispcntA &= 0xFFFF0000;
310		softwareRenderer->dispcntA |= value;
311		DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
312		break;
313	case DS9_REG_A_DISPCNT_HI:
314		softwareRenderer->dispcntA &= 0x0000FFFF;
315		softwareRenderer->dispcntA |= value << 16;
316		DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
317		break;
318	case DS9_REG_B_DISPCNT_LO:
319		softwareRenderer->dispcntB &= 0xFFFF0000;
320		softwareRenderer->dispcntB |= value;
321		DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
322		break;
323	case DS9_REG_B_DISPCNT_HI:
324		softwareRenderer->dispcntB &= 0x0000FFFF;
325		softwareRenderer->dispcntB |= value << 16;
326		DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
327		break;
328	case DS9_REG_POWCNT1:
329		value &= 0x810F;
330		softwareRenderer->powcnt = value;
331	}
332	return value;
333}
334
335static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
336	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
337	if (address < 0x400) {
338		softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
339	} else {
340		softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
341	}
342}
343
344static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
345	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
346	if (oam < 0x200) {
347		softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
348	} else {
349		softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
350	}
351}
352
353static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
354	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
355	_regenerateExtPalette(softwareRenderer, obj, engB, slot);
356}
357
358static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, struct DSGX* gx, int y) {
359	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
360
361	int x;
362	color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
363	if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
364		for (x = 0; x < softwareRenderer->masterEnd; ++x) {
365			row[x] = GBA_COLOR_WHITE;
366		}
367		return;
368	}
369
370	GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
371	int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
372
373	int w;
374	unsigned priority;
375	for (priority = 0; priority < 4; ++priority) {
376		softwareRenderer->end = 0;
377		for (w = 0; w < softwareRenderer->nWindows; ++w) {
378			softwareRenderer->start = softwareRenderer->end;
379			softwareRenderer->end = softwareRenderer->windows[w].endX;
380			softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
381			if (spriteLayers & (1 << priority)) {
382				GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
383			}
384			if (TEST_LAYER_ENABLED(0)) {
385				if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) {
386					color_t* scanline;
387					gx->renderer->getScanline(gx->renderer, y, &scanline);
388					uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
389					flags |= FLAG_TARGET_2 * softwareRenderer->bg[0].target2;
390					flags |= FLAG_TARGET_1 * (softwareRenderer->bg[0].target1 && softwareRenderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed));
391					int x;
392					for (x = softwareRenderer->start; x < softwareRenderer->end; ++x) {
393						if ((scanline[x] & FLAG_UNWRITTEN) != FLAG_UNWRITTEN) {
394							_compositeBlendNoObjwin(softwareRenderer, &softwareRenderer->row[x], scanline[x] | flags, softwareRenderer->row[x]);
395						}
396					}
397				} else {
398					GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
399				}
400			}
401			if (TEST_LAYER_ENABLED(1)) {
402				GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
403			}
404			if (TEST_LAYER_ENABLED(2)) {
405				switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
406				case 0:
407				case 1:
408				case 3:
409					GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
410					break;
411				case 2:
412				case 4:
413					GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
414					break;
415				case 5:
416					DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
417					break;
418				}
419			}
420			if (TEST_LAYER_ENABLED(3)) {
421				switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
422				case 0:
423					GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
424					break;
425				case 1:
426				case 2:
427					GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
428					break;
429				case 3:
430				case 4:
431				case 5:
432					DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
433					break;
434				}
435			}
436		}
437	}
438	softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
439	softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
440	softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
441	softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
442
443	GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
444
445#ifdef COLOR_16_BIT
446#if defined(__ARM_NEON) && !defined(__APPLE__)
447	_to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
448#else
449	for (x = 0; x < softwareRenderer->masterEnd; ++x) {
450		row[x] = softwareRenderer->row[x];
451	}
452#endif
453#else
454	switch (softwareRenderer->masterBright) {
455	case 0:
456	default:
457		memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
458		break;
459	case 1:
460		for (x = 0; x < softwareRenderer->masterEnd; ++x) {
461			row[x] = _brighten(softwareRenderer->row[x], softwareRenderer->masterBrightY);
462		}
463		break;
464	case 2:
465		for (x = 0; x < softwareRenderer->masterEnd; ++x) {
466			row[x] = _darken(softwareRenderer->row[x], softwareRenderer->masterBrightY);
467		}
468		break;
469	}
470#endif
471}
472
473static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
474	memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
475	memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
476	color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
477
478	int x;
479	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
480	case 0:
481		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
482			row[x] = GBA_COLOR_WHITE;
483		}
484		return;
485	case 1:
486		DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
487		return;
488	case 2: {
489		uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
490		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
491			color_t color;
492			LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
493#ifndef COLOR_16_BIT
494			unsigned color32 = 0;
495			color32 |= (color << 9) & 0xF80000;
496			color32 |= (color << 3) & 0xF8;
497			color32 |= (color << 6) & 0xF800;
498			color32 |= (color32 >> 5) & 0x070707;
499			color = color32;
500#elif COLOR_5_6_5
501			uint16_t color16 = 0;
502			color16 |= (color & 0x001F) << 11;
503			color16 |= (color & 0x03E0) << 1;
504			color16 |= (color & 0x7C00) >> 10;
505			color = color16;
506#endif
507			softwareRenderer->row[x] = color;
508		}
509		break;
510	}
511	case 3:
512		break;
513	}
514
515#ifdef COLOR_16_BIT
516#if defined(__ARM_NEON) && !defined(__APPLE__)
517	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
518#else
519	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
520		row[x] = softwareRenderer->row[x];
521	}
522#endif
523#else
524	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
525#endif
526}
527
528static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
529	memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
530	memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
531	color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
532
533	int x;
534	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
535	case 0:
536		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
537			row[x] = GBA_COLOR_WHITE;
538		}
539		return;
540	case 1:
541		DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, NULL, y);
542		return;
543	}
544
545#ifdef COLOR_16_BIT
546#if defined(__ARM_NEON) && !defined(__APPLE__)
547	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
548#else
549	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
550		row[x] = softwareRenderer->row[x];
551	}
552#endif
553#else
554	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
555#endif
556}
557
558static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
559	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
560	if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
561		softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
562		softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
563	} else {
564		softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
565		softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
566	}
567
568	_drawScanlineA(softwareRenderer, y);
569	_drawScanlineB(softwareRenderer, y);
570}
571
572static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
573	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
574	softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
575	softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
576}
577
578static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
579	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
580#ifdef COLOR_16_BIT
581#error Not yet supported
582#else
583	*stride = softwareRenderer->outputBufferStride;
584	*pixels = softwareRenderer->outputBuffer;
585#endif
586}
587
588static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
589}
590
591#define EXT_0_COORD_OVERFLOW \
592	localX = x & (sizeAdjusted - 1); \
593	localY = y & (sizeAdjusted - 1); \
594
595#define EXT_0_COORD_NO_OVERFLOW \
596	if ((x | y) & ~(sizeAdjusted - 1)) { \
597		continue; \
598	} \
599	localX = x; \
600	localY = y;
601
602#define EXT_0_NO_MOSAIC(COORD) \
603	COORD \
604	uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
605	uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
606	if (UNLIKELY(!screenBlock)) { \
607		continue; \
608	} \
609	LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
610	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
611	palette = &mainPalette[paletteData]; \
612	uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
613	uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
614	if (UNLIKELY(!vram)) { \
615		continue; \
616	} \
617	pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
618
619#define EXT_0_MOSAIC(COORD) \
620		if (!mosaicWait) { \
621			EXT_0_NO_MOSAIC(COORD) \
622			mosaicWait = mosaicH; \
623		} else { \
624			--mosaicWait; \
625		}
626
627#define EXT_0_LOOP(MOSAIC, COORD, BLEND, OBJWIN) \
628	for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
629		x += background->dx; \
630		y += background->dy; \
631		\
632		uint32_t current = *pixel; \
633		MOSAIC(COORD) \
634		if (pixelData) { \
635			COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
636		} \
637	}
638
639#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
640	if (background->overflow) { \
641		if (mosaicH > 1) { \
642			EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
643		} else { \
644			EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
645		} \
646	} else { \
647		if (mosaicH > 1) { \
648			EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
649		} else { \
650			EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
651		} \
652	}
653
654void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
655	int sizeAdjusted = 0x8000 << background->size;
656
657	BACKGROUND_BITMAP_INIT;
658
659	color_t* mainPalette = background->extPalette;
660	if (variant) {
661		palette = background->variantPalette;
662	}
663	int paletteData;
664
665	uint16_t mapData;
666	uint8_t pixelData = 0;
667
668	int outX;
669	uint32_t* pixel;
670
671	if (!objwinSlowPath) {
672		if (!(flags & FLAG_TARGET_2)) {
673			DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
674		} else {
675			DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
676		}
677	} else {
678		if (!(flags & FLAG_TARGET_2)) {
679			DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
680		} else {
681			DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
682		}
683	}
684}