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