all repos — mgba @ 2d7b1099a92a585dbe51026f2df36de00d7144df

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