all repos — mgba @ 46f01969d89c570cf47f9945ff8553022094379b

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
  8#include <mgba/internal/arm/macros.h>
  9#include <mgba/internal/ds/io.h>
 10
 11static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer);
 12static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer);
 13static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
 14static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
 15static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
 16static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
 17static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
 18static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
 19static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
 20static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
 21
 22void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
 23	renderer->d.init = DSVideoSoftwareRendererInit;
 24	renderer->d.reset = DSVideoSoftwareRendererReset;
 25	renderer->d.deinit = DSVideoSoftwareRendererDeinit;
 26	renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
 27	renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
 28	renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
 29	renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
 30	renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
 31	renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
 32	renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
 33
 34	renderer->engA.d.cache = NULL;
 35	GBAVideoSoftwareRendererCreate(&renderer->engA);
 36	renderer->engB.d.cache = NULL;
 37	GBAVideoSoftwareRendererCreate(&renderer->engB);
 38}
 39
 40static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
 41	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 42	softwareRenderer->engA.d.palette = &renderer->palette[0];
 43	softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
 44	softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
 45	softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
 46	softwareRenderer->engB.d.palette = &renderer->palette[512];
 47	softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
 48	softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
 49	softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
 50
 51	DSVideoSoftwareRendererReset(renderer);
 52}
 53
 54static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
 55	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 56	softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
 57	softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
 58	softwareRenderer->powcnt = 0;
 59	softwareRenderer->dispcntA = 0;
 60	softwareRenderer->dispcntB = 0;
 61}
 62
 63static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
 64	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 65	softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
 66	softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
 67}
 68
 69static void DSVideoSoftwareRendererUpdateDISPCNTA(struct DSVideoSoftwareRenderer* softwareRenderer) {
 70	uint16_t fakeDispcnt = softwareRenderer->dispcntA & 0xFF87;
 71	if (!DSRegisterDISPCNTIsTileObjMapping(softwareRenderer->dispcntA)) {
 72		softwareRenderer->engA.tileStride = 0x20;
 73	} else {
 74		softwareRenderer->engA.tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(softwareRenderer->dispcntA);
 75		fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
 76	}
 77	softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
 78	uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
 79	uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
 80	softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG0CNT, softwareRenderer->engA.bg[0].control);
 81	softwareRenderer->engA.bg[0].charBase += charBase;
 82	softwareRenderer->engA.bg[0].screenBase &= ~0x70000;
 83	softwareRenderer->engA.bg[0].screenBase |= screenBase;
 84	softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG1CNT, softwareRenderer->engA.bg[1].control);
 85	softwareRenderer->engA.bg[1].charBase += charBase;
 86	softwareRenderer->engA.bg[1].screenBase &= ~0x70000;
 87	softwareRenderer->engA.bg[1].screenBase |= screenBase;
 88	softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG2CNT, softwareRenderer->engA.bg[2].control);
 89	softwareRenderer->engA.bg[2].charBase += charBase;
 90	softwareRenderer->engA.bg[2].screenBase &= ~0x70000;
 91	softwareRenderer->engA.bg[2].screenBase |= screenBase;
 92	softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG3CNT, softwareRenderer->engA.bg[3].control);
 93	softwareRenderer->engA.bg[3].charBase += charBase;
 94	softwareRenderer->engA.bg[3].screenBase &= ~0x70000;
 95	softwareRenderer->engA.bg[3].screenBase |= screenBase;
 96}
 97
 98static void DSVideoSoftwareRendererUpdateDISPCNTB(struct DSVideoSoftwareRenderer* softwareRenderer) {
 99	uint16_t fakeDispcnt = softwareRenderer->dispcntB & 0xFF87;
100	if (!DSRegisterDISPCNTIsTileObjMapping(softwareRenderer->dispcntB)) {
101		softwareRenderer->engB.tileStride = 0x20;
102	} else {
103		softwareRenderer->engB.tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(softwareRenderer->dispcntB);
104		fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
105	}
106	softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
107}
108
109static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
110	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
111	if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
112		value = softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
113	} else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
114		value = softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
115	} else {
116		mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
117	}
118	switch (address) {
119	case DS9_REG_A_DISPCNT_LO:
120		softwareRenderer->dispcntA &= 0xFFFF0000;
121		softwareRenderer->dispcntA |= value;
122		DSVideoSoftwareRendererUpdateDISPCNTA(softwareRenderer);
123		break;
124	case DS9_REG_A_DISPCNT_HI:
125		softwareRenderer->dispcntA &= 0x0000FFFF;
126		softwareRenderer->dispcntA |= value << 16;
127		DSVideoSoftwareRendererUpdateDISPCNTA(softwareRenderer);
128		break;
129	case DS9_REG_B_DISPCNT_LO:
130		softwareRenderer->dispcntB &= 0xFFFF0000;
131		softwareRenderer->dispcntB |= value;
132		DSVideoSoftwareRendererUpdateDISPCNTB(softwareRenderer);
133		break;
134	case DS9_REG_B_DISPCNT_HI:
135		softwareRenderer->dispcntB &= 0x0000FFFF;
136		softwareRenderer->dispcntB |= value << 16;
137		DSVideoSoftwareRendererUpdateDISPCNTB(softwareRenderer);
138		break;
139	case DS9_REG_POWCNT1:
140		value &= 0x810F;
141		softwareRenderer->powcnt = value;
142	}
143	return value;
144}
145
146static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
147	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
148	if (address < 0x400) {
149		softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
150	} else {
151		softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
152	}
153}
154
155static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
156	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
157	if (oam < 0x200) {
158		softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
159	} else {
160		softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
161	}
162}
163
164static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
165	memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
166	memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
167	color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
168
169	int x;
170	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
171	case 0:
172		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
173			row[x] = GBA_COLOR_WHITE;
174		}
175		return;
176	case 1:
177		softwareRenderer->engA.d.drawScanline(&softwareRenderer->engA.d, y);
178		return;
179	case 2: {
180		uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
181		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
182			color_t color;
183			LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
184#ifndef COLOR_16_BIT
185			unsigned color32 = 0;
186			color32 |= (color << 9) & 0xF80000;
187			color32 |= (color << 3) & 0xF8;
188			color32 |= (color << 6) & 0xF800;
189			color32 |= (color32 >> 5) & 0x070707;
190			color = color32;
191#elif COLOR_5_6_5
192			uint16_t color16 = 0;
193			color16 |= (color & 0x001F) << 11;
194			color16 |= (color & 0x03E0) << 1;
195			color16 |= (color & 0x7C00) >> 10;
196			color = color16;
197#endif
198			softwareRenderer->row[x] = color;
199		}
200		break;
201	}
202	case 3:
203		break;
204	}
205
206#ifdef COLOR_16_BIT
207#if defined(__ARM_NEON) && !defined(__APPLE__)
208	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
209#else
210	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
211		row[x] = softwareRenderer->row[x];
212	}
213#endif
214#else
215	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
216#endif
217}
218
219static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
220	memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
221	memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
222	color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
223
224	int x;
225	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
226	case 0:
227		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
228			row[x] = GBA_COLOR_WHITE;
229		}
230		return;
231	case 1:
232		softwareRenderer->engB.d.drawScanline(&softwareRenderer->engB.d, y);
233		return;
234	}
235
236#ifdef COLOR_16_BIT
237#if defined(__ARM_NEON) && !defined(__APPLE__)
238	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
239#else
240	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
241		row[x] = softwareRenderer->row[x];
242	}
243#endif
244#else
245	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
246#endif
247}
248
249static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
250	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
251	if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
252		softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
253		softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
254	} else {
255		softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
256		softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
257	}
258
259	_drawScanlineA(softwareRenderer, y);
260	_drawScanlineB(softwareRenderer, y);
261}
262
263static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
264	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
265	softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
266	softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
267}
268
269static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
270	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
271#ifdef COLOR_16_BIT
272#error Not yet supported
273#else
274	*stride = softwareRenderer->outputBufferStride;
275	*pixels = softwareRenderer->outputBuffer;
276#endif
277}
278
279static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
280}