all repos — mgba @ fc74ad9ceb102a046db417dc7382b8954a7cc85e

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}
 59
 60static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
 61	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 62	softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
 63	softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
 64}
 65
 66static void GBAVideoSoftwareRendererUpdateDISPCNTA(struct DSVideoSoftwareRenderer* softwareRenderer) {
 67	uint16_t fakeDispcnt = softwareRenderer->dispcntA & 0xFF87;
 68	if (!DSRegisterDISPCNTIsTileObjMapping(softwareRenderer->dispcntA)) {
 69		softwareRenderer->engA.tileStride = 0x20;
 70	} else {
 71		softwareRenderer->engA.tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(softwareRenderer->dispcntA);
 72		fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
 73	}
 74	softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
 75}
 76
 77static void GBAVideoSoftwareRendererUpdateDISPCNTB(struct DSVideoSoftwareRenderer* softwareRenderer) {
 78	uint16_t fakeDispcnt = softwareRenderer->dispcntB & 0xFF87;
 79	if (!DSRegisterDISPCNTIsTileObjMapping(softwareRenderer->dispcntB)) {
 80		softwareRenderer->engB.tileStride = 0x20;
 81	} else {
 82		softwareRenderer->engB.tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(softwareRenderer->dispcntB);
 83		fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
 84	}
 85	softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
 86}
 87
 88static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
 89	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 90	if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
 91		value = softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
 92	} else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
 93		value = softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
 94	} else {
 95		mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
 96	}
 97	switch (address) {
 98	case DS9_REG_A_DISPCNT_LO:
 99		softwareRenderer->dispcntA &= 0xFFFF0000;
100		softwareRenderer->dispcntA |= value;
101		GBAVideoSoftwareRendererUpdateDISPCNTA(softwareRenderer);
102		break;
103	case DS9_REG_A_DISPCNT_HI:
104		softwareRenderer->dispcntA &= 0x0000FFFF;
105		softwareRenderer->dispcntA |= value << 16;
106		GBAVideoSoftwareRendererUpdateDISPCNTA(softwareRenderer);
107		break;
108	case DS9_REG_B_DISPCNT_LO:
109		softwareRenderer->dispcntB &= 0xFFFF0000;
110		softwareRenderer->dispcntB |= value;
111		GBAVideoSoftwareRendererUpdateDISPCNTB(softwareRenderer);
112		break;
113	case DS9_REG_B_DISPCNT_HI:
114		softwareRenderer->dispcntB &= 0x0000FFFF;
115		softwareRenderer->dispcntB |= value << 16;
116		GBAVideoSoftwareRendererUpdateDISPCNTB(softwareRenderer);
117		break;
118	case DS9_REG_POWCNT1:
119		value &= 0x810F;
120		softwareRenderer->powcnt = value;
121	}
122	return value;
123}
124
125static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
126	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
127	if (address < 0x400) {
128		softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
129	} else {
130		softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
131	}
132}
133
134static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
135	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
136	if (oam < 0x200) {
137		softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
138	} else {
139		softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
140	}
141}
142
143static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
144	memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
145	memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
146	color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
147
148	int x;
149	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
150	case 0:
151		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
152			row[x] = GBA_COLOR_WHITE;
153		}
154		return;
155	case 1:
156		softwareRenderer->engA.d.drawScanline(&softwareRenderer->engA.d, y);
157		return;
158	case 2: {
159		uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
160		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
161			color_t color;
162			LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
163#ifndef COLOR_16_BIT
164			unsigned color32 = 0;
165			color32 |= (color << 9) & 0xF80000;
166			color32 |= (color << 3) & 0xF8;
167			color32 |= (color << 6) & 0xF800;
168			color32 |= (color32 >> 5) & 0x070707;
169			color = color32;
170#elif COLOR_5_6_5
171			uint16_t color16 = 0;
172			color16 |= (color & 0x001F) << 11;
173			color16 |= (color & 0x03E0) << 1;
174			color16 |= (color & 0x7C00) >> 10;
175			color = color16;
176#endif
177			softwareRenderer->row[x] = color;
178		}
179		break;
180	}
181	case 3:
182		break;
183	}
184
185#ifdef COLOR_16_BIT
186#if defined(__ARM_NEON) && !defined(__APPLE__)
187	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
188#else
189	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
190		row[x] = softwareRenderer->row[x];
191	}
192#endif
193#else
194	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
195#endif
196}
197
198static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
199	memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
200	memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
201	color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
202
203	int x;
204	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
205	case 0:
206		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
207			row[x] = GBA_COLOR_WHITE;
208		}
209		return;
210	case 1:
211		softwareRenderer->engB.d.drawScanline(&softwareRenderer->engB.d, y);
212		return;
213	}
214
215#ifdef COLOR_16_BIT
216#if defined(__ARM_NEON) && !defined(__APPLE__)
217	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
218#else
219	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
220		row[x] = softwareRenderer->row[x];
221	}
222#endif
223#else
224	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
225#endif
226}
227
228static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
229	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
230	if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
231		softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
232		softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
233	} else {
234		softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
235		softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
236	}
237
238	_drawScanlineA(softwareRenderer, y);
239	_drawScanlineB(softwareRenderer, y);
240}
241
242static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
243	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
244	softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
245	softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
246}
247
248static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
249	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
250#ifdef COLOR_16_BIT
251#error Not yet supported
252#else
253	*stride = softwareRenderer->outputBufferStride;
254	*pixels = softwareRenderer->outputBuffer;
255#endif
256}
257
258static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
259}