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}