all repos — mgba @ 4fecedb42a6a27a6de2fb99b60de77914c15828e

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