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#include "gba/renderers/software-private.h"
8
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/ds/gx.h>
11#include <mgba/internal/ds/io.h>
12
13static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer);
14static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer);
15static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
16static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
17static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
18static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
19static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
20static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
21static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
22static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
23static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
24
25static void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
26
27static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool obj, bool engB, int slot) {
28 color_t* palette;
29 color_t* variantPalette;
30 struct GBAVideoSoftwareRenderer* softwareRenderer;
31 uint16_t* vram;
32 if (!obj) {
33 if (!engB) {
34 palette = &renderer->extPaletteA[slot * 4096];
35 variantPalette = &renderer->variantPaletteA[slot * 4096];
36 softwareRenderer = &renderer->engA;
37 vram = renderer->d.vramABGExtPal[slot];
38 } else {
39 palette = &renderer->extPaletteB[slot * 4096];
40 variantPalette = &renderer->variantPaletteB[slot * 4096];
41 softwareRenderer = &renderer->engB;
42 vram = renderer->d.vramBBGExtPal[slot];
43 }
44 } else {
45 if (!engB) {
46 palette = renderer->objExtPaletteA;
47 variantPalette = renderer->variantPaletteA;
48 softwareRenderer = &renderer->engA;
49 vram = renderer->d.vramAOBJExtPal;
50 } else {
51 palette = renderer->objExtPaletteB;
52 variantPalette = renderer->variantPaletteB;
53 softwareRenderer = &renderer->engB;
54 vram = renderer->d.vramBOBJExtPal;
55 }
56 }
57 if (!vram) {
58 return false;
59 }
60 int i;
61 for (i = 0; i < 4096; ++i) {
62 uint16_t value = vram[i];
63#ifdef COLOR_16_BIT
64#ifdef COLOR_5_6_5
65 unsigned color = 0;
66 color |= (value & 0x001F) << 11;
67 color |= (value & 0x03E0) << 1;
68 color |= (value & 0x7C00) >> 10;
69#else
70 unsigned color = value;
71#endif
72#else
73 unsigned color = 0;
74 color |= (value << 3) & 0xF8;
75 color |= (value << 6) & 0xF800;
76 color |= (value << 9) & 0xF80000;
77 color |= (color >> 5) & 0x070707;
78#endif
79 palette[i] = color;
80 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
81 variantPalette[i] = _brighten(color, softwareRenderer->bldy);
82 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
83 variantPalette[i] = _darken(color, softwareRenderer->bldy);
84 }
85 }
86 if (obj) {
87 softwareRenderer->objExtPalette = palette;
88 } else {
89 if (slot >= 2) {
90 if (GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot - 2].control)) {
91 softwareRenderer->bg[slot - 2].extPalette = palette;
92 softwareRenderer->bg[slot - 2].variantPalette = variantPalette;
93 }
94 } else if (slot < 2 && !GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot].control) ) {
95 softwareRenderer->bg[slot].extPalette = palette;
96 softwareRenderer->bg[slot].variantPalette = variantPalette;
97 }
98 softwareRenderer->bg[slot].extPalette = palette;
99 softwareRenderer->bg[slot].variantPalette = variantPalette;
100 }
101 return true;
102}
103
104static void _updateCharBase(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
105 struct GBAVideoSoftwareRenderer* eng;
106 if (!engB) {
107 eng = &softwareRenderer->engA;
108 } else {
109 eng = &softwareRenderer->engB;
110 }
111 int i;
112 uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
113 uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
114 for (i = 0; i < 4; ++i) {
115 if (!engB) {
116 uint32_t control = eng->bg[i].control;
117 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_BG0CNT + i * 2, control);
118 eng->bg[i].control = control;
119 }
120
121 eng->bg[i].charBase = GBARegisterBGCNTGetCharBase(eng->bg[i].control) << 14;
122
123 if (!engB) {
124 softwareRenderer->engA.bg[i].charBase += charBase;
125 softwareRenderer->engA.bg[i].screenBase &= ~0x70000;
126 softwareRenderer->engA.bg[i].screenBase |= screenBase;
127 }
128 }
129}
130
131
132void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
133 renderer->d.init = DSVideoSoftwareRendererInit;
134 renderer->d.reset = DSVideoSoftwareRendererReset;
135 renderer->d.deinit = DSVideoSoftwareRendererDeinit;
136 renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
137 renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
138 renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
139 renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
140 renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
141 renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
142 renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
143 renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
144
145 renderer->engA.d.cache = NULL;
146 GBAVideoSoftwareRendererCreate(&renderer->engA);
147 renderer->engB.d.cache = NULL;
148 GBAVideoSoftwareRendererCreate(&renderer->engB);
149}
150
151static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
152 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
153 softwareRenderer->engA.d.palette = &renderer->palette[0];
154 softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
155 softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
156 softwareRenderer->engA.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
157 softwareRenderer->engA.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
158 softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
159 softwareRenderer->engB.d.palette = &renderer->palette[512];
160 softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
161 softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
162 softwareRenderer->engB.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
163 softwareRenderer->engB.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
164 softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
165
166 DSVideoSoftwareRendererReset(renderer);
167}
168
169static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
170 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
171 softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
172 softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
173 softwareRenderer->powcnt = 0;
174 softwareRenderer->dispcntA = 0;
175 softwareRenderer->dispcntB = 0;
176}
177
178static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
179 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
180 softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
181 softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
182}
183
184static void DSVideoSoftwareRendererUpdateDISPCNT(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
185 uint32_t dispcnt;
186 struct GBAVideoSoftwareRenderer* eng;
187 if (!engB) {
188 dispcnt = softwareRenderer->dispcntA;
189 eng = &softwareRenderer->engA;
190 } else {
191 dispcnt = softwareRenderer->dispcntB;
192 eng = &softwareRenderer->engB;
193 }
194 uint16_t fakeDispcnt = dispcnt & 0xFF87;
195 if (!DSRegisterDISPCNTIsTileObjMapping(dispcnt)) {
196 eng->tileStride = 0x20;
197 } else {
198 eng->tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(dispcnt);
199 fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
200 }
201 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
202 eng->dispcnt |= dispcnt & 0xFFFF0000;
203 if (DSRegisterDISPCNTIsBgExtPalette(dispcnt)) {
204 color_t* extPalette;
205 if (!engB) {
206 extPalette = softwareRenderer->extPaletteA;
207 } else {
208 extPalette = softwareRenderer->extPaletteB;
209 }
210 int i;
211 for (i = 0; i < 4; ++i) {
212 int slot = i;
213 if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(eng->bg[i].control)) {
214 slot += 2;
215 }
216 if (eng->bg[i].extPalette != &extPalette[slot * 4096]) {
217 _regenerateExtPalette(softwareRenderer, false, engB, slot);
218 }
219 }
220 } else {
221 eng->bg[0].extPalette = NULL;
222 eng->bg[1].extPalette = NULL;
223 eng->bg[2].extPalette = NULL;
224 eng->bg[3].extPalette = NULL;
225 }
226 if (DSRegisterDISPCNTIsObjExtPalette(dispcnt)) {
227 if (!engB) {
228 if (softwareRenderer->engA.objExtPalette != softwareRenderer->objExtPaletteA) {
229 _regenerateExtPalette(softwareRenderer, true, engB, 0);
230 }
231 } else {
232 if (softwareRenderer->engB.objExtPalette != softwareRenderer->objExtPaletteB) {
233 _regenerateExtPalette(softwareRenderer, true, engB, 0);
234 }
235 }
236 } else {
237 if (!engB) {
238 softwareRenderer->engA.objExtPalette = NULL;
239 } else {
240 softwareRenderer->engB.objExtPalette = NULL;
241 }
242 }
243 if (!engB) {
244 eng->dispcnt = DSRegisterDISPCNTClear3D(eng->dispcnt);
245 eng->dispcnt |= DSRegisterDISPCNTIs3D(dispcnt);
246 _updateCharBase(softwareRenderer, engB);
247 }
248}
249
250static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
251 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
252 if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
253 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
254 } else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
255 softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
256 } else {
257 mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
258 }
259 switch (address) {
260 case DS9_REG_A_BG0CNT:
261 case DS9_REG_A_BG1CNT:
262 softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
263 // Fall through
264 case DS9_REG_A_BG2CNT:
265 case DS9_REG_A_BG3CNT:
266 _updateCharBase(softwareRenderer, false);
267 break;
268 case DS9_REG_B_BG0CNT:
269 case DS9_REG_B_BG1CNT:
270 softwareRenderer->engB.bg[(address - DS9_REG_B_BG0CNT) >> 1].control = value;
271 // Fall through
272 case DS9_REG_B_BG2CNT:
273 case DS9_REG_B_BG3CNT:
274 _updateCharBase(softwareRenderer, true);
275 break;
276 case DS9_REG_A_MASTER_BRIGHT:
277 softwareRenderer->engA.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
278 softwareRenderer->engA.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
279 if (softwareRenderer->engA.masterBrightY > 0x10) {
280 softwareRenderer->engA.masterBrightY = 0x10;
281 }
282 break;
283 case DS9_REG_B_MASTER_BRIGHT:
284 softwareRenderer->engB.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
285 softwareRenderer->engB.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
286 if (softwareRenderer->engB.masterBrightY > 0x10) {
287 softwareRenderer->engB.masterBrightY = 0x10;
288 }
289 break;
290 case DS9_REG_A_BLDCNT:
291 case DS9_REG_A_BLDY:
292 // TODO: Optimize
293 _regenerateExtPalette(softwareRenderer, false, false, 0);
294 _regenerateExtPalette(softwareRenderer, false, false, 1);
295 _regenerateExtPalette(softwareRenderer, false, false, 2);
296 _regenerateExtPalette(softwareRenderer, false, false, 3);
297 _regenerateExtPalette(softwareRenderer, true, false, 0);
298 break;
299 case DS9_REG_B_BLDCNT:
300 case DS9_REG_B_BLDY:
301 // TODO: Optimize
302 _regenerateExtPalette(softwareRenderer, false, true, 0);
303 _regenerateExtPalette(softwareRenderer, false, true, 1);
304 _regenerateExtPalette(softwareRenderer, false, true, 2);
305 _regenerateExtPalette(softwareRenderer, false, true, 3);
306 _regenerateExtPalette(softwareRenderer, true, true, 0);
307 break;
308 case DS9_REG_A_DISPCNT_LO:
309 softwareRenderer->dispcntA &= 0xFFFF0000;
310 softwareRenderer->dispcntA |= value;
311 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
312 break;
313 case DS9_REG_A_DISPCNT_HI:
314 softwareRenderer->dispcntA &= 0x0000FFFF;
315 softwareRenderer->dispcntA |= value << 16;
316 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
317 break;
318 case DS9_REG_B_DISPCNT_LO:
319 softwareRenderer->dispcntB &= 0xFFFF0000;
320 softwareRenderer->dispcntB |= value;
321 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
322 break;
323 case DS9_REG_B_DISPCNT_HI:
324 softwareRenderer->dispcntB &= 0x0000FFFF;
325 softwareRenderer->dispcntB |= value << 16;
326 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
327 break;
328 case DS9_REG_POWCNT1:
329 value &= 0x810F;
330 softwareRenderer->powcnt = value;
331 }
332 return value;
333}
334
335static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
336 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
337 if (address < 0x400) {
338 softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
339 } else {
340 softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
341 }
342}
343
344static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
345 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
346 if (oam < 0x200) {
347 softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
348 } else {
349 softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
350 }
351}
352
353static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
354 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
355 _regenerateExtPalette(softwareRenderer, obj, engB, slot);
356}
357
358static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, struct DSGX* gx, int y) {
359 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
360
361 int x;
362 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
363 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
364 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
365 row[x] = GBA_COLOR_WHITE;
366 }
367 return;
368 }
369
370 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
371 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
372
373 int w;
374 unsigned priority;
375 for (priority = 0; priority < 4; ++priority) {
376 softwareRenderer->end = 0;
377 for (w = 0; w < softwareRenderer->nWindows; ++w) {
378 softwareRenderer->start = softwareRenderer->end;
379 softwareRenderer->end = softwareRenderer->windows[w].endX;
380 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
381 if (spriteLayers & (1 << priority)) {
382 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
383 }
384 if (TEST_LAYER_ENABLED(0)) {
385 if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) {
386 color_t* scanline;
387 gx->renderer->getScanline(gx->renderer, y, &scanline);
388 uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
389 flags |= FLAG_TARGET_2 * softwareRenderer->bg[0].target2;
390 flags |= FLAG_TARGET_1 * (softwareRenderer->bg[0].target1 && softwareRenderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed));
391 int x;
392 for (x = softwareRenderer->start; x < softwareRenderer->end; ++x) {
393 if ((scanline[x] & FLAG_UNWRITTEN) != FLAG_UNWRITTEN) {
394 _compositeBlendNoObjwin(softwareRenderer, &softwareRenderer->row[x], scanline[x] | flags, softwareRenderer->row[x]);
395 }
396 }
397 } else {
398 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
399 }
400 }
401 if (TEST_LAYER_ENABLED(1)) {
402 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
403 }
404 if (TEST_LAYER_ENABLED(2)) {
405 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
406 case 0:
407 case 1:
408 case 3:
409 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
410 break;
411 case 2:
412 case 4:
413 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
414 break;
415 case 5:
416 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
417 break;
418 }
419 }
420 if (TEST_LAYER_ENABLED(3)) {
421 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
422 case 0:
423 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
424 break;
425 case 1:
426 case 2:
427 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
428 break;
429 case 3:
430 case 4:
431 case 5:
432 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
433 break;
434 }
435 }
436 }
437 }
438 softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
439 softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
440 softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
441 softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
442
443 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
444
445#ifdef COLOR_16_BIT
446#if defined(__ARM_NEON) && !defined(__APPLE__)
447 _to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
448#else
449 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
450 row[x] = softwareRenderer->row[x];
451 }
452#endif
453#else
454 switch (softwareRenderer->masterBright) {
455 case 0:
456 default:
457 memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
458 break;
459 case 1:
460 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
461 row[x] = _brighten(softwareRenderer->row[x], softwareRenderer->masterBrightY);
462 }
463 break;
464 case 2:
465 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
466 row[x] = _darken(softwareRenderer->row[x], softwareRenderer->masterBrightY);
467 }
468 break;
469 }
470#endif
471}
472
473static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
474 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
475 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
476 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
477
478 int x;
479 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
480 case 0:
481 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
482 row[x] = GBA_COLOR_WHITE;
483 }
484 return;
485 case 1:
486 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
487 return;
488 case 2: {
489 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
490 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
491 color_t color;
492 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
493#ifndef COLOR_16_BIT
494 unsigned color32 = 0;
495 color32 |= (color << 9) & 0xF80000;
496 color32 |= (color << 3) & 0xF8;
497 color32 |= (color << 6) & 0xF800;
498 color32 |= (color32 >> 5) & 0x070707;
499 color = color32;
500#elif COLOR_5_6_5
501 uint16_t color16 = 0;
502 color16 |= (color & 0x001F) << 11;
503 color16 |= (color & 0x03E0) << 1;
504 color16 |= (color & 0x7C00) >> 10;
505 color = color16;
506#endif
507 softwareRenderer->row[x] = color;
508 }
509 break;
510 }
511 case 3:
512 break;
513 }
514
515#ifdef COLOR_16_BIT
516#if defined(__ARM_NEON) && !defined(__APPLE__)
517 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
518#else
519 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
520 row[x] = softwareRenderer->row[x];
521 }
522#endif
523#else
524 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
525#endif
526}
527
528static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
529 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
530 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
531 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
532
533 int x;
534 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
535 case 0:
536 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
537 row[x] = GBA_COLOR_WHITE;
538 }
539 return;
540 case 1:
541 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, NULL, y);
542 return;
543 }
544
545#ifdef COLOR_16_BIT
546#if defined(__ARM_NEON) && !defined(__APPLE__)
547 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
548#else
549 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
550 row[x] = softwareRenderer->row[x];
551 }
552#endif
553#else
554 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
555#endif
556}
557
558static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
559 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
560 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
561 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
562 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
563 } else {
564 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
565 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
566 }
567
568 _drawScanlineA(softwareRenderer, y);
569 _drawScanlineB(softwareRenderer, y);
570}
571
572static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
573 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
574 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
575 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
576}
577
578static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
579 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
580#ifdef COLOR_16_BIT
581#error Not yet supported
582#else
583 *stride = softwareRenderer->outputBufferStride;
584 *pixels = softwareRenderer->outputBuffer;
585#endif
586}
587
588static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
589}
590
591#define EXT_0_COORD_OVERFLOW \
592 localX = x & (sizeAdjusted - 1); \
593 localY = y & (sizeAdjusted - 1); \
594
595#define EXT_0_COORD_NO_OVERFLOW \
596 if ((x | y) & ~(sizeAdjusted - 1)) { \
597 continue; \
598 } \
599 localX = x; \
600 localY = y;
601
602#define EXT_0_NO_MOSAIC(COORD) \
603 COORD \
604 uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
605 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
606 if (UNLIKELY(!screenBlock)) { \
607 continue; \
608 } \
609 LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
610 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
611 palette = &mainPalette[paletteData]; \
612 uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
613 uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
614 if (UNLIKELY(!vram)) { \
615 continue; \
616 } \
617 pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
618
619#define EXT_0_MOSAIC(COORD) \
620 if (!mosaicWait) { \
621 EXT_0_NO_MOSAIC(COORD) \
622 mosaicWait = mosaicH; \
623 } else { \
624 --mosaicWait; \
625 }
626
627#define EXT_0_LOOP(MOSAIC, COORD, BLEND, OBJWIN) \
628 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
629 x += background->dx; \
630 y += background->dy; \
631 \
632 uint32_t current = *pixel; \
633 MOSAIC(COORD) \
634 if (pixelData) { \
635 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
636 } \
637 }
638
639#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
640 if (background->overflow) { \
641 if (mosaicH > 1) { \
642 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
643 } else { \
644 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
645 } \
646 } else { \
647 if (mosaicH > 1) { \
648 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
649 } else { \
650 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
651 } \
652 }
653
654void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
655 int sizeAdjusted = 0x8000 << background->size;
656
657 BACKGROUND_BITMAP_INIT;
658
659 color_t* mainPalette = background->extPalette;
660 if (variant) {
661 palette = background->variantPalette;
662 }
663 int paletteData;
664
665 uint16_t mapData;
666 uint8_t pixelData = 0;
667
668 int outX;
669 uint32_t* pixel;
670
671 if (!objwinSlowPath) {
672 if (!(flags & FLAG_TARGET_2)) {
673 DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
674 } else {
675 DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
676 }
677 } else {
678 if (!(flags & FLAG_TARGET_2)) {
679 DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
680 } else {
681 DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
682 }
683 }
684}