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}