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
376 int w;
377 unsigned priority;
378 for (priority = 0; priority < 4; ++priority) {
379 softwareRenderer->end = 0;
380 for (w = 0; w < softwareRenderer->nWindows; ++w) {
381 softwareRenderer->start = softwareRenderer->end;
382 softwareRenderer->end = softwareRenderer->windows[w].endX;
383 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
384 if (spriteLayers & (1 << priority)) {
385 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
386 }
387 if (TEST_LAYER_ENABLED(0)) {
388 if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) {
389 const color_t* scanline;
390 gx->renderer->getScanline(gx->renderer, y, &scanline);
391 uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
392 flags |= FLAG_TARGET_2 * softwareRenderer->bg[0].target2;
393 flags |= FLAG_TARGET_1 * (softwareRenderer->bg[0].target1 && softwareRenderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed));
394 int x;
395 for (x = softwareRenderer->start; x < softwareRenderer->end; ++x) {
396 if (scanline[x] & 0xF8000000) {
397 _compositeBlendNoObjwin(softwareRenderer, &softwareRenderer->row[x], (scanline[x] & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
398 }
399 }
400 } else {
401 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
402 }
403 }
404 if (TEST_LAYER_ENABLED(1)) {
405 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
406 }
407 if (TEST_LAYER_ENABLED(2)) {
408 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
409 case 0:
410 case 1:
411 case 3:
412 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
413 break;
414 case 2:
415 case 4:
416 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
417 break;
418 case 5:
419 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[2].control)) {
420 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
421 } else {
422 // TODO
423 }
424 break;
425 }
426 }
427 if (TEST_LAYER_ENABLED(3)) {
428 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
429 case 0:
430 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
431 break;
432 case 1:
433 case 2:
434 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
435 break;
436 case 3:
437 case 4:
438 case 5:
439 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[3].control)) {
440 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
441 } else {
442 // TODO
443 }
444 break;
445 }
446 }
447 }
448 }
449 softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
450 softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
451 softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
452 softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
453
454 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
455}
456
457static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
458 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
459 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
460 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
461
462 int x;
463 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
464 case 0:
465 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
466 row[x] = GBA_COLOR_WHITE;
467 }
468 return;
469 case 1:
470 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
471 break;
472 case 2: {
473 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
474 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
475 color_t color;
476 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
477#ifndef COLOR_16_BIT
478 unsigned color32 = 0;
479 color32 |= (color << 9) & 0xF80000;
480 color32 |= (color << 3) & 0xF8;
481 color32 |= (color << 6) & 0xF800;
482 color32 |= (color32 >> 5) & 0x070707;
483 color = color32;
484#elif COLOR_5_6_5
485 uint16_t color16 = 0;
486 color16 |= (color & 0x001F) << 11;
487 color16 |= (color & 0x03E0) << 1;
488 color16 |= (color & 0x7C00) >> 10;
489 color = color16;
490#endif
491 softwareRenderer->engA.row[x] = color;
492 }
493 break;
494 }
495 case 3:
496 break;
497 }
498
499#ifdef COLOR_16_BIT
500#if defined(__ARM_NEON) && !defined(__APPLE__)
501 _to16Bit(row, softwareRenderer->engA.row, DS_VIDEO_HORIZONTAL_PIXELS);
502#else
503 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
504 row[x] = softwareRenderer->engA.row[x];
505 }
506#endif
507#else
508 switch (softwareRenderer->engA.masterBright) {
509 case 0:
510 default:
511 memcpy(row, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*row));
512 break;
513 case 1:
514 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
515 row[x] = _brighten(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
516 }
517 break;
518 case 2:
519 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
520 row[x] = _darken(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
521 }
522 break;
523 }
524#endif
525}
526
527static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
528 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
529 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
530 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
531
532 int x;
533 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
534 case 0:
535 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
536 row[x] = GBA_COLOR_WHITE;
537 }
538 return;
539 case 1:
540 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, NULL, y);
541 break;
542 }
543
544#ifdef COLOR_16_BIT
545#if defined(__ARM_NEON) && !defined(__APPLE__)
546 _to16Bit(row, softwareRenderer->engB.row, DS_VIDEO_HORIZONTAL_PIXELS);
547#else
548 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
549 row[x] = softwareRenderer->engB.row[x];
550 }
551#endif
552#else
553 switch (softwareRenderer->engB.masterBright) {
554 case 0:
555 default:
556 memcpy(row, softwareRenderer->engB.row, softwareRenderer->engB.masterEnd * sizeof(*row));
557 break;
558 case 1:
559 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
560 row[x] = _brighten(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
561 }
562 break;
563 case 2:
564 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
565 row[x] = _darken(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
566 }
567 break;
568 }
569#endif
570}
571
572static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
573 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
574 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
575 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
576 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
577 } else {
578 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
579 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
580 }
581
582 _drawScanlineA(softwareRenderer, y);
583 _drawScanlineB(softwareRenderer, y);
584}
585
586static void DSVideoSoftwareRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline) {
587 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
588 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
589 memcpy(scanline, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*scanline));
590}
591
592static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
593 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
594 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
595 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
596}
597
598static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
599 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
600#ifdef COLOR_16_BIT
601#error Not yet supported
602#else
603 *stride = softwareRenderer->outputBufferStride;
604 *pixels = softwareRenderer->outputBuffer;
605#endif
606}
607
608static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
609}
610
611#define EXT_0_COORD_OVERFLOW \
612 localX = x & (sizeAdjusted - 1); \
613 localY = y & (sizeAdjusted - 1); \
614
615#define EXT_0_COORD_NO_OVERFLOW \
616 if ((x | y) & ~(sizeAdjusted - 1)) { \
617 continue; \
618 } \
619 localX = x; \
620 localY = y;
621
622#define EXT_0_NO_MOSAIC(COORD) \
623 COORD \
624 uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
625 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
626 if (UNLIKELY(!screenBlock)) { \
627 continue; \
628 } \
629 LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
630 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
631 palette = &mainPalette[paletteData]; \
632 uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
633 uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
634 if (UNLIKELY(!vram)) { \
635 continue; \
636 } \
637 pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
638
639#define EXT_0_MOSAIC(COORD) \
640 if (!mosaicWait) { \
641 EXT_0_NO_MOSAIC(COORD) \
642 mosaicWait = mosaicH; \
643 } else { \
644 --mosaicWait; \
645 }
646
647#define EXT_0_LOOP(MOSAIC, COORD, BLEND, OBJWIN) \
648 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
649 x += background->dx; \
650 y += background->dy; \
651 \
652 uint32_t current = *pixel; \
653 MOSAIC(COORD) \
654 if (pixelData) { \
655 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
656 } \
657 }
658
659#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
660 if (background->overflow) { \
661 if (mosaicH > 1) { \
662 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
663 } else { \
664 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
665 } \
666 } else { \
667 if (mosaicH > 1) { \
668 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
669 } else { \
670 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
671 } \
672 }
673
674void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
675 int sizeAdjusted = 0x8000 << background->size;
676
677 BACKGROUND_BITMAP_INIT;
678
679 color_t* mainPalette = background->extPalette;
680 if (variant) {
681 mainPalette = background->variantPalette;
682 }
683 if (!mainPalette) {
684 return;
685 }
686 int paletteData;
687
688 uint16_t mapData;
689 uint8_t pixelData = 0;
690
691 int outX;
692 uint32_t* pixel;
693
694 if (!objwinSlowPath) {
695 if (!(flags & FLAG_TARGET_2)) {
696 DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
697 } else {
698 DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
699 }
700 } else {
701 if (!(flags & FLAG_TARGET_2)) {
702 DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
703 } else {
704 DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
705 }
706 }
707}