all repos — mgba @ c0498276b534663b1496b0b346aa454ae35e5d4b

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