all repos — mgba @ 3f27b09ec162439b252f30246746b16577b990b0

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