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