all repos — mgba @ f32fbd737fee9defcfbbb73a386685f936eeaae5

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