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}