all repos — mgba @ e7d6cba3616f00d6b633eecdfad5b89b5a15846c

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