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}