all repos — mgba @ 5fd2f0c43adc426efd366e61de3486518fb33eec

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