Port of the atari800 emulator to the 3DS.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

video.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * 3ds/video.c - Nintendo 3DS video backend
  3. *
  4. * Copyright (c) 2001-2002 Jacek Poplawski
  5. * Copyright (C) 2001-2016 Atari800 development team (see DOC/CREDITS)
  6. *
  7. * This file is part of the Atari800 emulator project which emulates
  8. * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
  9. *
  10. * Atari800 is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * Atari800 is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with Atari800; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. */
  24. #include <3ds.h>
  25. #include "artifact.h"
  26. #include "atari.h"
  27. #include "colours.h"
  28. #include "config.h"
  29. #include "input.h"
  30. #include "input_3ds.h"
  31. #include "log.h"
  32. #ifdef PAL_BLENDING
  33. #include "pal_blending.h"
  34. #endif /* PAL_BLENDING */
  35. #include "platform.h"
  36. #include "screen.h"
  37. #include "util.h"
  38. #include "videomode.h"
  39. #include "grapefruit.h"
  40. #include "shader_shbin.h"
  41. static C3D_Tex tex, kbd_display;
  42. static C3D_Mtx proj_top, proj_bottom;
  43. static C3D_RenderTarget *target_top, *target_bottom;
  44. static struct ctr_shader_data shader;
  45. static u32 *texBuf;
  46. VIDEOMODE_MODE_t N3DS_VIDEO_mode;
  47. static int ctable[256];
  48. // #define SOFTWARE_INTERLAVE
  49. #ifdef SOFTWARE_INTERLEAVE
  50. static u8 morton_lut[64] = {
  51. 0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
  52. 0x02, 0x03, 0x06, 0x07, 0x12, 0x13, 0x16, 0x17,
  53. 0x08, 0x09, 0x0c, 0x0d, 0x18, 0x19, 0x1c, 0x1d,
  54. 0x0a, 0x0b, 0x0e, 0x0f, 0x1a, 0x1b, 0x1e, 0x1f,
  55. 0x20, 0x21, 0x24, 0x25, 0x30, 0x31, 0x34, 0x35,
  56. 0x22, 0x23, 0x26, 0x27, 0x32, 0x33, 0x36, 0x37,
  57. 0x28, 0x29, 0x2c, 0x2d, 0x38, 0x39, 0x3c, 0x3d,
  58. 0x2a, 0x2b, 0x2e, 0x2f, 0x3a, 0x3b, 0x3e, 0x3f
  59. };
  60. static s8 morton_delta_y[8] = {
  61. 0x02, 0x06, 0x02, 0x16, 0x02, 0x06, 0x02, 0x16
  62. };
  63. static void N3DS_RenderMorton8to32(u8 *src, u32 *dest)
  64. {
  65. int x, y, xm, ym;
  66. u8 *rsrc;
  67. u32 *rdest;
  68. for (y = 0; y < VIDEOMODE_src_height; y+=8) {
  69. rdest = dest + (y << 9);
  70. for (x = 0; x < VIDEOMODE_src_width; x+=8) {
  71. rsrc = src + (Screen_WIDTH * y) + x;
  72. for (ym = 0; ym < 8; ym++) {
  73. rdest[0x00] = ctable[*(rsrc++)];
  74. rdest[0x01] = ctable[*(rsrc++)];
  75. rdest[0x04] = ctable[*(rsrc++)];
  76. rdest[0x05] = ctable[*(rsrc++)];
  77. rdest[0x10] = ctable[*(rsrc++)];
  78. rdest[0x11] = ctable[*(rsrc++)];
  79. rdest[0x14] = ctable[*(rsrc++)];
  80. rdest[0x15] = ctable[*(rsrc)];
  81. rsrc += (Screen_WIDTH - 7);
  82. rdest += morton_delta_y[ym];
  83. }
  84. }
  85. }
  86. }
  87. static void N3DS_RenderMorton32to32(u32 *src, u32 *dest, u32 pitch, u32 width, u32 height)
  88. {
  89. int x, y, xm, ym;
  90. u32 *rsrc;
  91. u32 *rdest;
  92. for (y = 0; y < height; y+=8) {
  93. rdest = dest + (y << 9);
  94. for (x = 0; x < width; x+=8) {
  95. rsrc = src + (pitch * y) + x;
  96. for (ym = 0; ym < 8; ym++) {
  97. rdest[0x00] = *(rsrc++);
  98. rdest[0x01] = *(rsrc++);
  99. rdest[0x04] = *(rsrc++);
  100. rdest[0x05] = *(rsrc++);
  101. rdest[0x10] = *(rsrc++);
  102. rdest[0x11] = *(rsrc++);
  103. rdest[0x14] = *(rsrc++);
  104. rdest[0x15] = *(rsrc);
  105. rsrc += (pitch - 7);
  106. rdest += morton_delta_y[ym];
  107. }
  108. }
  109. }
  110. }
  111. #endif
  112. void N3DS_VIDEO_PaletteUpdate()
  113. {
  114. int i;
  115. for (i = 0; i < 256; i++)
  116. {
  117. ctable[i] = Colours_table[i] << 8 | 0xFF;
  118. }
  119. }
  120. static void N3DS_RenderNormal(u8 *src, u32 *dest)
  121. {
  122. int x, y;
  123. int spitch = Screen_WIDTH - VIDEOMODE_src_width;
  124. int dpitch = 512 - VIDEOMODE_src_width;
  125. for (y = 0; y < VIDEOMODE_src_height; y++) {
  126. for (x = 0; x < VIDEOMODE_src_width; x++) {
  127. *(dest++) = ctable[*(src++)];
  128. }
  129. src += spitch;
  130. dest += dpitch;
  131. }
  132. }
  133. void N3DS_InitVideo(void)
  134. {
  135. C3D_TexEnv* texEnv;
  136. gfxInitDefault();
  137. gfxSet3D(false);
  138. C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
  139. target_top = C3D_RenderTargetCreate(240, 400, GPU_RB_RGB8, GPU_RB_DEPTH16);
  140. target_bottom = C3D_RenderTargetCreate(240, 320, GPU_RB_RGB8, GPU_RB_DEPTH16);
  141. C3D_RenderTargetSetClear(target_top, C3D_CLEAR_ALL, 0, 0);
  142. C3D_RenderTargetSetClear(target_bottom, C3D_CLEAR_ALL, 0, 0);
  143. C3D_RenderTargetSetOutput(target_top, GFX_TOP, GFX_LEFT,
  144. GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
  145. C3D_RenderTargetSetOutput(target_bottom, GFX_BOTTOM, GFX_LEFT,
  146. GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8));
  147. #ifdef SOFTWARE_INTERLAVE
  148. C3D_TexInit(&tex, 512, 256, GPU_RGBA8);
  149. #else
  150. C3D_TexInit(&tex, 512, 256, GPU_RGBA8);
  151. #endif
  152. texBuf = linearAlloc(512 * 256 * 4);
  153. ctr_load_png(&kbd_display, "romfs:/kbd_display.png", TEXTURE_TARGET_VRAM);
  154. ctr_init_shader(&shader, shader_shbin, shader_shbin_size);
  155. AttrInfo_AddLoader(&(shader.attr), 0, GPU_FLOAT, 3); // v0 = position
  156. AttrInfo_AddLoader(&(shader.attr), 1, GPU_FLOAT, 2); // v1 = texcoord
  157. ctr_bind_shader(&shader);
  158. Mtx_OrthoTilt(&proj_top, 0.0, 400.0, 0.0, 240.0, -1.0, 1.0, true);
  159. Mtx_OrthoTilt(&proj_bottom, 0.0, 320.0, 0.0, 240.0, -1.0, 1.0, true);
  160. texEnv = C3D_GetTexEnv(0);
  161. C3D_TexEnvSrc(texEnv, C3D_Both, GPU_TEXTURE0, 0, 0);
  162. C3D_TexEnvOp(texEnv, C3D_Both, 0, 0, 0);
  163. C3D_TexEnvFunc(texEnv, C3D_Both, GPU_MODULATE);
  164. C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL);
  165. }
  166. void N3DS_ExitVideo(void)
  167. {
  168. linearFree(texBuf);
  169. C3D_TexDelete(&kbd_display);
  170. C3D_TexDelete(&tex);
  171. C3D_RenderTargetDelete(target_top);
  172. C3D_RenderTargetDelete(target_bottom);
  173. C3D_Fini();
  174. gfxExit();
  175. }
  176. void PLATFORM_PaletteUpdate(void)
  177. {
  178. #ifdef PAL_BLENDING
  179. if (N3DS_VIDEO_mode == VIDEOMODE_MODE_NORMAL && ARTIFACT_mode == ARTIFACT_PAL_BLEND)
  180. PAL_BLENDING_UpdateLookup();
  181. #endif
  182. N3DS_VIDEO_PaletteUpdate();
  183. }
  184. void PLATFORM_GetPixelFormat(PLATFORM_pixel_format_t *format)
  185. {
  186. format->bpp = 32;
  187. format->rmask = 0xFF000000;
  188. format->gmask = 0x00FF0000;
  189. format->bmask = 0x0000FF00;
  190. }
  191. void PLATFORM_MapRGB(void *dest, int const *palette, int size)
  192. {
  193. int i;
  194. u32* target = (u32*) dest;
  195. for (i = 0; i < 256; i++)
  196. {
  197. target[i] = (palette[i] << 8) | 0xFF;
  198. }
  199. }
  200. void PLATFORM_SetVideoMode(VIDEOMODE_resolution_t const *res, int windowed, VIDEOMODE_MODE_t mode, int rotate90)
  201. {
  202. N3DS_VIDEO_mode = mode;
  203. PLATFORM_PaletteUpdate();
  204. PLATFORM_DisplayScreen();
  205. }
  206. VIDEOMODE_resolution_t *PLATFORM_AvailableResolutions(unsigned int *size)
  207. {
  208. VIDEOMODE_resolution_t *resolutions;
  209. resolutions = Util_malloc(1 * sizeof(VIDEOMODE_resolution_t));
  210. resolutions[0].width = 400;
  211. resolutions[0].height = 240;
  212. *size = 1;
  213. return resolutions;
  214. }
  215. VIDEOMODE_resolution_t *PLATFORM_DesktopResolution(void)
  216. {
  217. VIDEOMODE_resolution_t *resolutions;
  218. resolutions = Util_malloc(1 * sizeof(VIDEOMODE_resolution_t));
  219. resolutions[0].width = 400;
  220. resolutions[0].height = 240;
  221. return resolutions;
  222. }
  223. int PLATFORM_SupportsVideomode(VIDEOMODE_MODE_t mode, int stretch, int rotate90)
  224. {
  225. if (rotate90 != 0)
  226. return false;
  227. return mode == VIDEOMODE_MODE_NORMAL;
  228. }
  229. int PLATFORM_WindowMaximised(void)
  230. {
  231. return 1;
  232. }
  233. void N3DS_DrawTexture(C3D_Tex* tex, int x, int y, int tx, int ty, int width, int height) {
  234. float txmin, tymin, txmax, tymax;
  235. txmin = (float) tx / tex->width;
  236. tymax = (float) ty / tex->height;
  237. txmax = (float) (tx+width) / tex->width;
  238. tymin = (float) (ty+height) / tex->height;
  239. C3D_TexBind(0, tex);
  240. C3D_ImmDrawBegin(GPU_TRIANGLE_STRIP);
  241. C3D_ImmSendAttrib((float) x, (float) 240 - y - height, 0.0f, 0.0f);
  242. C3D_ImmSendAttrib((float) txmin, (float) tymin, 0.0f, 0.0f);
  243. C3D_ImmSendAttrib((float) x + width, (float) 240 - y - height, 0.0f, 0.0f);
  244. C3D_ImmSendAttrib((float) txmax, (float) tymin, 0.0f, 0.0f);
  245. C3D_ImmSendAttrib((float) x, (float) 240 - y, 0.0f, 0.0f);
  246. C3D_ImmSendAttrib((float) txmin, (float) tymax, 0.0f, 0.0f);
  247. C3D_ImmSendAttrib((float) x + width, (float) 240 - y, 0.0f, 0.0f);
  248. C3D_ImmSendAttrib((float) txmax, (float) tymax, 0.0f, 0.0f);
  249. C3D_ImmDrawEnd();
  250. }
  251. void PLATFORM_DisplayScreen(void)
  252. {
  253. u8 *src;
  254. #ifdef SOFTWARE_INTERLAVE
  255. u32 *dest;
  256. #endif
  257. float xmin, ymin, xmax, ymax, txmin, tymin, txmax, tymax;
  258. src = (u8*) Screen_atari;
  259. src += Screen_WIDTH * VIDEOMODE_src_offset_top + VIDEOMODE_src_offset_left;
  260. #ifdef SOFTWARE_INTERLAVE
  261. dest = (u32*) tex.data;
  262. if (!C3D_FrameBegin(0))
  263. return;
  264. #endif
  265. #ifdef PAL_BLENDING
  266. if (N3DS_VIDEO_mode == VIDEOMODE_MODE_NORMAL && ARTIFACT_mode == ARTIFACT_PAL_BLEND)
  267. {
  268. #ifdef SOFTWARE_INTERLAVE
  269. PAL_BLENDING_Blit32(texBuf, src, Screen_WIDTH, VIDEOMODE_src_width, VIDEOMODE_src_height, VIDEOMODE_src_offset_top % 2);
  270. N3DS_RenderMorton32to32(texBuf, dest, Screen_WIDTH, VIDEOMODE_src_width, VIDEOMODE_src_height);
  271. #else
  272. PAL_BLENDING_Blit32(texBuf, src, tex.width, VIDEOMODE_src_width, VIDEOMODE_src_height, VIDEOMODE_src_offset_top % 2);
  273. #endif
  274. }
  275. else
  276. #endif
  277. {
  278. #ifdef SOFTWARE_INTERLAVE
  279. N3DS_RenderMorton8to32(src, dest);
  280. #else
  281. N3DS_RenderNormal(src, texBuf);
  282. #endif
  283. }
  284. #ifdef SOFTWARE_INTERLAVE
  285. GSPGPU_FlushDataCache(dest, 512 * 256 * 4);
  286. #else
  287. GSPGPU_FlushDataCache(texBuf, 512 * 256 * 4);
  288. C3D_SafeDisplayTransfer(texBuf, GX_BUFFER_DIM(512, 256), tex.data, GX_BUFFER_DIM(tex.width, tex.height),
  289. (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) |
  290. GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) |
  291. GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
  292. );
  293. gspWaitForPPF();
  294. if (!C3D_FrameBegin(0))
  295. return;
  296. GSPGPU_FlushDataCache(tex.data, 512 * 256 * 4);
  297. #endif
  298. C3D_FrameDrawOn(target_bottom);
  299. C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, shader.proj_loc, &proj_bottom);
  300. C3D_TexBind(0, &kbd_display);
  301. N3DS_DrawKeyboard(&kbd_display);
  302. xmin = (400 - VIDEOMODE_dest_width) / 2.0f;
  303. ymin = (240 - VIDEOMODE_dest_height) / 2.0f;
  304. xmax = xmin + VIDEOMODE_dest_width;
  305. ymax = ymin + VIDEOMODE_dest_height;
  306. txmax = ((float) VIDEOMODE_src_width / tex.width);
  307. txmin = 0.0f;
  308. #ifdef SOFTWARE_INTERLEAVE
  309. tymin = 1.0f - ((float) VIDEOMODE_src_height / tex.height);
  310. tymax = 1.0f;
  311. #else
  312. tymin = ((float) VIDEOMODE_src_height / tex.height);
  313. tymax = 0.0f;
  314. #endif
  315. C3D_FrameDrawOn(target_top);
  316. C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, shader.proj_loc, &proj_top);
  317. C3D_TexBind(0, &tex);
  318. C3D_ImmDrawBegin(GPU_TRIANGLE_STRIP);
  319. C3D_ImmSendAttrib(xmin, ymin, 0.0f, 0.0f);
  320. C3D_ImmSendAttrib(txmin, tymin, 0.0f, 0.0f);
  321. C3D_ImmSendAttrib(xmax, ymin, 0.0f, 0.0f);
  322. C3D_ImmSendAttrib(txmax, tymin, 0.0f, 0.0f);
  323. C3D_ImmSendAttrib(xmin, ymax, 0.0f, 0.0f);
  324. C3D_ImmSendAttrib(txmin, tymax, 0.0f, 0.0f);
  325. C3D_ImmSendAttrib(xmax, ymax, 0.0f, 0.0f);
  326. C3D_ImmSendAttrib(txmax, tymax, 0.0f, 0.0f);
  327. C3D_ImmDrawEnd();
  328. C3D_FrameEnd(0);
  329. }