all repos — mgba @ e0518fdf30601b2424ab98e7b351cb75d66bcd74

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 DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
 17static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
 18static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
 19static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
 20
 21void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
 22	renderer->d.init = DSVideoSoftwareRendererInit;
 23	renderer->d.reset = DSVideoSoftwareRendererReset;
 24	renderer->d.deinit = DSVideoSoftwareRendererDeinit;
 25	renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
 26	renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
 27	renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
 28	renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
 29	renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
 30	renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
 31
 32	renderer->engA.d.cache = NULL;
 33	GBAVideoSoftwareRendererCreate(&renderer->engA);
 34	renderer->engB.d.cache = NULL;
 35	GBAVideoSoftwareRendererCreate(&renderer->engB);
 36}
 37
 38static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
 39	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 40	softwareRenderer->engA.d.palette = &renderer->palette[0];
 41	softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
 42	// TODO: VRAM
 43	softwareRenderer->engB.d.palette = &renderer->palette[512];
 44	softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
 45	// TODO: VRAM
 46
 47	DSVideoSoftwareRendererReset(renderer);
 48}
 49
 50static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
 51	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 52	softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
 53	softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
 54}
 55
 56static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
 57	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 58	softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
 59	softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
 60}
 61
 62static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
 63	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 64	if (address <= DS9_REG_A_BLDY) {
 65		value = softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
 66	} else if (address >= DS9_REG_B_DISPCNT_LO && address <= DS9_REG_B_BLDY) {
 67		value = softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
 68	} else {
 69		mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
 70	}
 71	switch (address) {
 72	case DS9_REG_A_DISPCNT_LO:
 73		softwareRenderer->dispcntA &= 0xFFFF0000;
 74		softwareRenderer->dispcntA |= value;
 75		break;
 76	case DS9_REG_A_DISPCNT_HI:
 77		softwareRenderer->dispcntA &= 0x0000FFFF;
 78		softwareRenderer->dispcntA |= value << 16;
 79		break;
 80	case DS9_REG_B_DISPCNT_LO:
 81		softwareRenderer->dispcntB &= 0xFFFF0000;
 82		softwareRenderer->dispcntB |= value;
 83		break;
 84	case DS9_REG_B_DISPCNT_HI:
 85		softwareRenderer->dispcntB &= 0x0000FFFF;
 86		softwareRenderer->dispcntB |= value << 16;
 87		break;
 88	}
 89	return value;
 90}
 91
 92static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
 93	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
 94	if (address < 0x200) {
 95		softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x1FF, value);
 96	} else {
 97		softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x1FF, value);		
 98	}
 99}
100
101static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
102	memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
103	memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
104	color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
105
106	int x;
107	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
108	case 0:
109		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
110			row[x] = GBA_COLOR_WHITE;
111		}
112		return;
113	case 1:
114		softwareRenderer->engA.d.drawScanline(&softwareRenderer->engA.d, y);
115		return;
116	case 2: {
117		uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
118		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
119			color_t color;
120			LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
121#ifndef COLOR_16_BIT
122			unsigned color32 = 0;
123			color32 |= (color << 9) & 0xF80000;
124			color32 |= (color << 3) & 0xF8;
125			color32 |= (color << 6) & 0xF800;
126			color32 |= (color32 >> 5) & 0x070707;
127			color = color32;
128#elif COLOR_5_6_5
129			uint16_t color16 = 0;
130			color16 |= (color & 0x001F) << 11;
131			color16 |= (color & 0x03E0) << 1;
132			color16 |= (color & 0x7C00) >> 10;
133			color = color16;
134#endif
135			softwareRenderer->row[x] = color;
136		}
137		break;
138	}
139	case 3:
140		break;
141	}
142
143#ifdef COLOR_16_BIT
144#if defined(__ARM_NEON) && !defined(__APPLE__)
145	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
146#else
147	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
148		row[x] = softwareRenderer->row[x];
149	}
150#endif
151#else
152	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
153#endif
154}
155
156static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
157	memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
158	memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
159	color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
160
161	int x;
162	switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
163	case 0:
164		for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
165			row[x] = GBA_COLOR_WHITE;
166		}
167		return;
168	case 1:
169		softwareRenderer->engB.d.drawScanline(&softwareRenderer->engB.d, y);
170		return;
171	}
172
173#ifdef COLOR_16_BIT
174#if defined(__ARM_NEON) && !defined(__APPLE__)
175	_to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
176#else
177	for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
178		row[x] = softwareRenderer->row[x];
179	}
180#endif
181#else
182	memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
183#endif
184}
185
186static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
187	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
188	softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
189	softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
190	softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
191	softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
192
193
194	_drawScanlineA(softwareRenderer, y);
195	_drawScanlineB(softwareRenderer, y);
196}
197
198static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
199	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
200	softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
201	softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
202}
203
204static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
205	struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
206#ifdef COLOR_16_BIT
207#error Not yet supported
208#else
209	*stride = softwareRenderer->outputBufferStride;
210	*pixels = softwareRenderer->outputBuffer;
211#endif
212}
213
214static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
215}