NESe (pronounced "Nessie") is a NES emulator based on the e6502 emulator, also written in C with a focus on speed and portability for use on embedded platforms, especially ARM.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

723 lignes
22KB

  1. #include <stdio.h>
  2. #include <SDL.h>
  3. #include "render.h"
  4. #include "ppu.h"
  5. #include "mapper.h"
  6. #include "menu.h"
  7. #include "sdl_overlay.h"
  8. #include "sdl_effect.h"
  9. static SDL_Color nes_palette[64] = {
  10. {0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6},
  11. {0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00},
  12. {0x7B,0x2B,0x00}, {0x00,0x3E,0x00}, {0x00,0x48,0x0D}, {0x00,0x3C,0x22},
  13. {0x00,0x2F,0x66}, {0x00,0x00,0x00}, {0x05,0x05,0x05}, {0x05,0x05,0x05},
  14. {0xC8,0xC8,0xC8}, {0x00,0x59,0xFF}, {0x44,0x3C,0xFF}, {0xB7,0x33,0xCC},
  15. {0xFF,0x33,0xAA}, {0xFF,0x37,0x5E}, {0xFF,0x37,0x1A}, {0xD5,0x4B,0x00},
  16. {0xC4,0x62,0x00}, {0x3C,0x7B,0x00}, {0x1E,0x84,0x15}, {0x00,0x95,0x66},
  17. {0x00,0x84,0xC4}, {0x11,0x11,0x11}, {0x09,0x09,0x09}, {0x09,0x09,0x09},
  18. {0xFF,0xFF,0xFF}, {0x00,0x95,0xFF}, {0x6F,0x84,0xFF}, {0xD5,0x6F,0xFF},
  19. {0xFF,0x77,0xCC}, {0xFF,0x6F,0x99}, {0xFF,0x7B,0x59}, {0xFF,0x91,0x5F},
  20. {0xFF,0xA2,0x33}, {0xA6,0xBF,0x00}, {0x51,0xD9,0x6A}, {0x4D,0xD5,0xAE},
  21. {0x00,0xD9,0xFF}, {0x66,0x66,0x66}, {0x0D,0x0D,0x0D}, {0x0D,0x0D,0x0D},
  22. {0xFF,0xFF,0xFF}, {0x84,0xBF,0xFF}, {0xBB,0xBB,0xFF}, {0xD0,0xBB,0xFF},
  23. {0xFF,0xBF,0xEA}, {0xFF,0xBF,0xCC}, {0xFF,0xC4,0xB7}, {0xFF,0xCC,0xAE},
  24. {0xFF,0xD9,0xA2}, {0xCC,0xE1,0x99}, {0xAE,0xEE,0xB7}, {0xAA,0xF7,0xEE},
  25. {0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11}
  26. };
  27. static inline uint8_t* chr_mem(const nes_ppu* ppu,
  28. uint16_t addr) {
  29. return ppu->mapper->chr_addr(ppu->map_data, addr);
  30. }
  31. typedef struct {
  32. SDL_Window* window;
  33. SDL_Renderer* renderer;
  34. SDL_Surface* background;
  35. SDL_Surface* foreground;
  36. SDL_Surface* target;
  37. sdl_overlay_font font;
  38. SDL_Texture* texture;
  39. SDL_Rect view;
  40. struct sdl_effect* effect;
  41. } sdl_render_data;
  42. static sdl_render_data the_render_data = {0};
  43. static void sdl_render_refresh(nes_Renderer* rend) {
  44. sdl_render_data* data = (sdl_render_data*)rend->data;
  45. int win_w = 0, win_h = 0;
  46. SDL_GetWindowSize(data->window, &win_w, &win_h);
  47. // Determine the viewport within the screen
  48. int w = win_w;
  49. int h = win_h;
  50. if (!(rend->flags & (1 << State_Bit_Integer_Scale))) {
  51. // Fullscreen
  52. if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) {
  53. w = (h * nes_ppu_scan_w) / nes_ppu_scan_h;
  54. } else {
  55. h = (w * nes_ppu_scan_h) / nes_ppu_scan_w;
  56. }
  57. } else {
  58. // Integer Scale
  59. int scale_y = 1, scale_x = 1;
  60. if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) {
  61. // Wide Window
  62. // printf("Wide\n");
  63. scale_y = h / nes_ppu_scan_h;
  64. scale_x = ( (scale_y * nes_ppu_scan_w) +
  65. (nes_ppu_render_w / 2) ) /
  66. nes_ppu_render_w;
  67. if ((scale_x * nes_ppu_render_w) > w) {
  68. scale_x = w / nes_ppu_render_w;
  69. }
  70. } else {
  71. // Tall Window
  72. // printf("Tall\n");
  73. scale_x = w / nes_ppu_render_w;
  74. scale_y = ( (scale_x * nes_ppu_render_w) +
  75. (nes_ppu_scan_w / 2) ) / nes_ppu_scan_w;
  76. if ((scale_y * nes_ppu_render_h) > h) {
  77. scale_y = h / nes_ppu_render_h;
  78. }
  79. }
  80. h = scale_y * nes_ppu_render_h;
  81. w = scale_x * nes_ppu_render_w;
  82. /*
  83. printf("Scale %d, %d : %d, %d -> %d, %d\n",
  84. scale_x, scale_y, win_w, win_h, w, h);
  85. */
  86. if (!(rend->flags & (1 << State_Bit_Fullscreen))) {
  87. SDL_SetWindowSize(data->window, w, h);
  88. int x = 0, y = 0;
  89. SDL_GetWindowPosition(data->window, &x, &y);
  90. SDL_SetWindowPosition(data->window,
  91. x - ((w - win_w) / 2),
  92. y - ((h - win_h) / 2));
  93. }
  94. }
  95. data->view.x = (win_w - w) / 2;
  96. data->view.y = (win_h - h) / 2;
  97. data->view.w = w;
  98. data->view.h = h;
  99. SDL_RenderSetClipRect(data->renderer, &data->view);
  100. }
  101. static int sdl_render_init(nes_Renderer* rend) {
  102. sdl_render_data* data = &the_render_data;
  103. rend->data = &the_render_data;
  104. int status = SDL_Init(SDL_INIT_VIDEO);
  105. if (0 != status) {
  106. fprintf(stderr, "SDL: Failed to initialize\n");
  107. } else {
  108. SDL_ShowCursor(0);
  109. SDL_DisplayMode mode = {0};
  110. SDL_GetCurrentDisplayMode(0, &mode);
  111. data->window = SDL_CreateWindow(
  112. "NESe",
  113. SDL_WINDOWPOS_CENTERED,
  114. SDL_WINDOWPOS_CENTERED,
  115. mode.w, mode.h, 0
  116. );
  117. if (NULL == data->window) {
  118. fprintf(stderr, "SDL: Failed to create window\n");
  119. SDL_Quit();
  120. status = -1;
  121. }
  122. }
  123. if (0 == status) {
  124. data->renderer = SDL_CreateRenderer(
  125. data->window, -1,
  126. SDL_RENDERER_ACCELERATED |
  127. SDL_RENDERER_PRESENTVSYNC
  128. );
  129. if (NULL == data->renderer) {
  130. fprintf(stderr, "SDL: Failed to create renderer\n");
  131. SDL_DestroyWindow(data->window);
  132. SDL_Quit();
  133. status = -1;
  134. } else {
  135. sdl_render_refresh(rend);
  136. overlay_init(&rend->overlay);
  137. sdl_overlay_font_init(data->renderer, &data->font);
  138. }
  139. }
  140. if (0 == status) {
  141. data->background = SDL_CreateRGBSurfaceWithFormat(
  142. 0, nes_ppu_render_w, nes_ppu_render_h,
  143. 8, SDL_PIXELFORMAT_INDEX8
  144. );
  145. if (NULL == data->background) {
  146. fprintf(stderr, "SDL: Failed to create background\n");
  147. SDL_DestroyRenderer(data->renderer);
  148. SDL_DestroyWindow(data->window);
  149. SDL_Quit();
  150. status = -1;
  151. }
  152. }
  153. if (0 == status) {
  154. data->foreground = SDL_CreateRGBSurfaceWithFormat(
  155. 0, nes_ppu_render_w, nes_ppu_render_h,
  156. 8, SDL_PIXELFORMAT_INDEX8
  157. );
  158. if (NULL == data->foreground) {
  159. fprintf(stderr, "SDL: Failed to create foreground\n");
  160. SDL_FreeSurface(data->background);
  161. SDL_DestroyRenderer(data->renderer);
  162. SDL_DestroyWindow(data->window);
  163. SDL_Quit();
  164. status = -1;
  165. }
  166. }
  167. if (0 == status) {
  168. data->texture = SDL_CreateTexture(
  169. data->renderer, SDL_PIXELFORMAT_RGB888,
  170. SDL_TEXTUREACCESS_STREAMING,
  171. nes_ppu_render_w, nes_ppu_render_h
  172. );
  173. if (NULL == data->texture) {
  174. fprintf(stderr, "SDL: Failed to create target\n");
  175. SDL_FreeSurface(data->foreground);
  176. SDL_FreeSurface(data->background);
  177. SDL_DestroyRenderer(data->renderer);
  178. SDL_DestroyWindow(data->window);
  179. SDL_Quit();
  180. status = -1;
  181. } else {
  182. SDL_LockTextureToSurface(data->texture, NULL,
  183. &data->target);
  184. SDL_FillRect(data->target, NULL, color_background);
  185. }
  186. }
  187. if (0 == status) {
  188. data->effect = effect_init(
  189. data->renderer,
  190. nes_ppu_render_w, nes_ppu_render_h
  191. );
  192. SDL_SetPaletteColors(data->background->format->palette,
  193. nes_palette, 0U, 64U);
  194. SDL_SetColorKey(data->background, SDL_TRUE, 0xFFU);
  195. SDL_SetPaletteColors(data->foreground->format->palette,
  196. nes_palette, 0U, 64U);
  197. SDL_SetColorKey(data->foreground, SDL_TRUE, 0xFFU);
  198. }
  199. return status;
  200. }
  201. static void sdl_render_done(nes_Renderer* rend) {
  202. sdl_render_data* data = (sdl_render_data*)rend->data;
  203. if (data->effect) effect_done(data->effect);
  204. overlay_done(&rend->overlay);
  205. sdl_overlay_font_done(&data->font);
  206. SDL_DestroyTexture(data->texture);
  207. SDL_FreeSurface(data->foreground);
  208. SDL_FreeSurface(data->background);
  209. SDL_DestroyRenderer(data->renderer);
  210. SDL_DestroyWindow(data->window);
  211. SDL_Quit();
  212. }
  213. static void sdl_render_set_flags(nes_Renderer* rend,
  214. uint32_t flags) {
  215. sdl_render_data* data = (sdl_render_data*)rend->data;
  216. SDL_SetWindowFullscreen(
  217. data->window,
  218. (flags & (1 << State_Bit_Fullscreen)) ?
  219. SDL_WINDOW_FULLSCREEN : 0
  220. );
  221. rend->flags = flags;
  222. sdl_render_refresh(rend);
  223. }
  224. static inline void render_sprite_line(
  225. const nes_ppu* ppu, int index, int y, const uint8_t* pal,
  226. uint8_t* dst, int start, int end, const uint8_t* back) {
  227. uint8_t* sprite = chr_mem(ppu, index * 16U);
  228. uint8_t lo = sprite[0U + y] << start;
  229. uint8_t hi = sprite[8U + y] << start;
  230. for (int x = start; x < end; ++x) {
  231. if (back && *back != 0xFFU) {
  232. *dst = *back;
  233. } else {
  234. int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7));
  235. if (pal_idx) *dst = pal[pal_idx];
  236. }
  237. if (back) ++back;
  238. dst++;
  239. hi <<= 1;
  240. lo <<= 1;
  241. }
  242. }
  243. static inline void render_sprite_line_flip(
  244. const nes_ppu* ppu, int index, int y, const uint8_t* pal,
  245. uint8_t* dst, int start, int end, const uint8_t* back) {
  246. uint8_t* sprite = chr_mem(ppu, index * 16U);
  247. uint8_t lo = sprite[0U + y] >> start;
  248. uint8_t hi = sprite[8U + y] >> start;
  249. for (int x = start; x < end; ++x) {
  250. if (back && *back != 0xFFU) {
  251. *dst = *back;
  252. } else {
  253. int pal_idx = (((hi & 1) << 1) | (lo & 1));
  254. if (pal_idx) *dst = pal[pal_idx];
  255. }
  256. if (back) ++back;
  257. dst++;
  258. hi >>= 1;
  259. lo >>= 1;
  260. }
  261. }
  262. static inline void render_bg_sprite_line(
  263. const nes_ppu* ppu, int index, int y, const uint8_t* pal,
  264. uint8_t* dst, int start, int end) {
  265. uint8_t* sprite = chr_mem(ppu, index * 16U);
  266. uint8_t lo = sprite[0U + y] << (start % 8);
  267. uint8_t hi = sprite[8U + y] << (start % 8);
  268. for (int x = start; x < end; ++x) {
  269. int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7));
  270. *dst = (pal_idx ? pal[pal_idx] : 0xFFU);
  271. dst++;
  272. hi <<= 1;
  273. lo <<= 1;
  274. }
  275. }
  276. static inline void render_bg_scanline_area(
  277. const nes_ppu* ppu, int page,
  278. uint8_t* dst, int x, int y, int w) {
  279. int block_x = x / 8;
  280. int line_end = x + w;
  281. int block_y = y / 8;
  282. y = y % 8;
  283. int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0;
  284. const uint8_t* indexes = nes_map_vram_addr(ppu->mapper,
  285. ppu->map_data,
  286. page << 10);
  287. const uint8_t* attrs = indexes + 960U;
  288. const uint8_t* index = indexes +
  289. (block_y * nes_ppu_blocks_w) +
  290. block_x;
  291. while (x < line_end) {
  292. int attr_idx = ((block_y / 4) * 8) + (block_x / 4);
  293. int shift = 2 * ((block_y & 0b10) |
  294. ((block_x & 0b10) >> 1));
  295. int pal_idx = (attrs[attr_idx] >> shift) & 3;
  296. const uint8_t* pal = &ppu->palette[pal_idx * 4];
  297. int end = (x + 8) & ~7;
  298. if (end > line_end) end = line_end;
  299. render_bg_sprite_line(ppu, bank + *index, y,
  300. pal, dst, x, end);
  301. ++index;
  302. ++block_x;
  303. dst += (end - x);
  304. x = end;
  305. }
  306. }
  307. static void render_bg_scanline(const nes_ppu* ppu,/* int scanline,*/
  308. uint8_t* dst) {
  309. int page = (ppu->control & ppu_Control_Nametable_Mask);
  310. int x = ppu->scroll_x;
  311. int y = ppu->scroll_y;
  312. if (y >= nes_ppu_render_h) y -= nes_ppu_render_h;
  313. int w = (nes_ppu_render_w - x);
  314. if (!(ppu->mask & ppu_Mask_Left_Back)) {
  315. // Handle column 0 flag - need to fill with transparency
  316. memset(dst, 0xFFU, 8);
  317. w -= 8;
  318. x += 8;
  319. dst += 8;
  320. }
  321. render_bg_scanline_area(ppu, page, dst, x, y, w);
  322. render_bg_scanline_area(ppu, page ^ 1, dst + w, 0, y,
  323. nes_ppu_render_w - w);
  324. }
  325. static void render_line_sprites(nes_ppu* ppu, uint8_t* dst_line,
  326. int scanline, const uint8_t* back,
  327. const oam_sprite* sprites,
  328. int n_sprites) {
  329. for (int i_sprite = n_sprites - 1; i_sprite >= 0; --i_sprite) {
  330. const oam_sprite* sprite = &sprites[i_sprite];
  331. int index = sprite->index;
  332. int bank = (ppu->control & ppu_Control_Sprite_Bank) ?
  333. 0x100 : 0;
  334. if (ppu->control & ppu_Control_Sprite_Size) {
  335. bank = (index & 1) ? 0x100 : 0;
  336. index &= 0xFEU;
  337. }
  338. index += bank;
  339. int y = scanline - (sprite->y + 1);
  340. if (ppu->control & ppu_Control_Sprite_Size) {
  341. if (y >= 8) {
  342. index ^= 1;
  343. y -= 8;
  344. }
  345. if (sprite->attr & oam_Attr_Flip_Y) {
  346. index ^= 1;
  347. }
  348. }
  349. int pal_idx = (sprite->attr & oam_Attr_Pal_Mask);
  350. const uint8_t* pal = &ppu->palette[16 + (pal_idx * 4)];
  351. if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y;
  352. int end = nes_ppu_render_w - sprite->x;
  353. if (end > 8) end = 8;
  354. int start = 0;
  355. if ( !(ppu->mask & ppu_Mask_Left_Sprite) &&
  356. sprite->x < 8) {
  357. start = 8 - sprite->x;
  358. }
  359. if (sprite->attr & oam_Attr_Flip_X) {
  360. render_sprite_line_flip(
  361. ppu, index, y, pal,
  362. dst_line + sprite->x + start, start, end,
  363. (sprite->attr & oam_Attr_Background) ?
  364. (back + sprite->x + start) : NULL
  365. );
  366. } else {
  367. render_sprite_line(
  368. ppu, index, y, pal,
  369. dst_line + sprite->x + start, start, end,
  370. (sprite->attr & oam_Attr_Background) ?
  371. (back + sprite->x + start) : NULL
  372. );
  373. }
  374. }
  375. }
  376. // Check sprite (0 only) collision on a scanline
  377. // This assumes that we've verified that this sprite
  378. // intersects with this scanline.
  379. // Scanline is 0-239 from inside the rendering window
  380. // (though we should never see this called with 0).
  381. static int eval_sprite_line(const nes_ppu* ppu, int y,
  382. const oam_sprite* sprite,
  383. const uint8_t* chr,
  384. const uint8_t* back) {
  385. int hit_pos = -1;
  386. // TODO: Handle Column 0 mask
  387. if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y;
  388. uint8_t lo = chr[0U + y];
  389. uint8_t hi = chr[8U + y];
  390. back += sprite->x;
  391. int end = nes_ppu_render_w;
  392. if (end > sprite->x + 8) end = sprite->x + 8;
  393. for (int x = sprite->x; x < end; ++x) {
  394. int pal_idx = (sprite->attr & oam_Attr_Flip_X) ?
  395. (((hi & 1) << 1) | (lo & 1)) :
  396. (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7));
  397. if (pal_idx && *back != 0xFFU) {
  398. hit_pos = x;
  399. break;
  400. }
  401. ++back;
  402. if (sprite->attr & oam_Attr_Flip_X) {
  403. hi >>= 1;
  404. lo >>= 1;
  405. } else {
  406. hi <<= 1;
  407. lo <<= 1;
  408. }
  409. }
  410. return hit_pos;
  411. }
  412. static void update_scanline_hit(nes_ppu* ppu, uint8_t* back_line,
  413. int scanline) {
  414. const oam_sprite* sprite = &ppu->oam[0];
  415. int bank = (ppu->control & ppu_Control_Sprite_Bank) ?
  416. 0x100 : 0;
  417. if ( !(ppu->mask & ppu_Mask_Left_Sprite) &&
  418. sprite->x < 8) {
  419. return;
  420. }
  421. int y_pos = (sprite->y + 1);
  422. int y = scanline - y_pos;
  423. if (0 > y) return;
  424. int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8;
  425. if (y >= h) return;
  426. int index = sprite->index;
  427. if (ppu->control & ppu_Control_Sprite_Size) {
  428. bank = (index & 1) ? 0x100 : 0;
  429. index &= 0xFEU;
  430. }
  431. index += bank;
  432. if (ppu->control & ppu_Control_Sprite_Size) {
  433. if (y >= 8) {
  434. index ^= 1;
  435. y -= 8;
  436. }
  437. if (sprite->attr & oam_Attr_Flip_Y) {
  438. index ^= 1;
  439. }
  440. }
  441. const uint8_t* chr = chr_mem(ppu, index * 16U);
  442. int hit = eval_sprite_line(ppu, y, sprite, chr, back_line);
  443. if (hit >= 0) {
  444. REND_LOG("Upcoming hit @ %d, %d\n", scanline + 1, hit);
  445. REND_LOG("(Currently @ %d, %d)\n", ppu->scanline, ppu->cycle);
  446. ppu->hit_line = scanline;
  447. ppu->hit_dot = hit;
  448. }
  449. }
  450. static int select_line_sprites(const nes_ppu* ppu, int scanline,
  451. oam_sprite* sprites, int max) {
  452. int n_sprites = 0;
  453. for ( int i_sprite = 0;
  454. i_sprite < nes_ppu_oam_sprite_count && n_sprites < max;
  455. ++i_sprite) {
  456. const oam_sprite* sprite = &ppu->oam[i_sprite];
  457. int y_pos = (sprite->y + 1);
  458. int y = scanline - y_pos;
  459. if (0 > y) continue;
  460. int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8;
  461. if (y >= h) continue;
  462. *sprites = *sprite;
  463. ++sprites;
  464. ++n_sprites;
  465. }
  466. return n_sprites;
  467. }
  468. static void dump_line_sprites(const nes_ppu* ppu, int line,
  469. const oam_sprite* sprites, int n) {
  470. while (n-- > 0) {
  471. PPU_LOG("PPU: Line %3d: Sprite $%02x @ %3d, %3d (%02x)\n",
  472. line, sprites->index, sprites->x, sprites->y,
  473. sprites->attr);
  474. ++sprites;
  475. }
  476. }
  477. static void render_scanline(nes_ppu* ppu, int line,
  478. sdl_render_data* data) {
  479. SDL_Rect dst_rect = {
  480. .x = 0,
  481. .y = line,
  482. .w = nes_ppu_render_w,
  483. .h = 1,
  484. };
  485. if (line >= 0) {
  486. // Emulate the happy part of the backdrop override quirk
  487. int pal_idx = (ppu->addr >= nes_ppu_mem_pal_start) ?
  488. (ppu->addr & (nes_ppu_mem_pal_size - 1)) : 0;
  489. SDL_Color ext = nes_palette[ppu->palette[pal_idx]];
  490. SDL_FillRect(data->target, &dst_rect, ((int)ext.r << 16) |
  491. ((int)ext.g << 8) |
  492. ext.b);
  493. }
  494. if (!(ppu->mask & (ppu_Mask_Sprite | ppu_Mask_Back))) {
  495. // Do nothing if BOTH are disabled.
  496. return;
  497. }
  498. SDL_Rect src_rect = {
  499. .x = 0,
  500. .y = 0,
  501. .w = nes_ppu_render_w,
  502. .h = 1,
  503. };
  504. uint8_t* background = data->background->pixels;
  505. // We check for hits if EITHER layer is enabled.
  506. render_bg_scanline(ppu, background);
  507. if (ppu->hit_line <= 0) {
  508. update_scanline_hit(ppu, background, line);
  509. }
  510. oam_sprite line_sprites[8] = {0};
  511. int n_sprites = select_line_sprites(ppu, line,
  512. line_sprites, 8);
  513. dump_line_sprites(ppu, line, line_sprites, n_sprites);
  514. if (ppu->mask & ppu_Mask_Back) {
  515. SDL_BlitSurface(data->background, &src_rect,
  516. data->target, &dst_rect);
  517. }
  518. if (ppu->mask & ppu_Mask_Sprite) {
  519. uint8_t* foreground = data->foreground->pixels;
  520. memset(foreground, 0xFFU, nes_ppu_render_w);
  521. render_line_sprites(ppu, foreground, line, background,
  522. line_sprites, n_sprites);
  523. SDL_BlitSurface(data->foreground, &src_rect,
  524. data->target, &dst_rect);
  525. }
  526. }
  527. static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) {
  528. int status = 0;
  529. sdl_render_data* data = (sdl_render_data*)rend->data;
  530. if (ppu->scanline < nes_ppu_prerender) {
  531. REND_LOG("Scanline %3d -> Prerender\n", ppu->scanline);
  532. // TODO: Perform evaluations here?
  533. } else if (ppu->scanline < nes_ppu_prerender +
  534. nes_ppu_height) {
  535. REND_LOG("Scanline %3d : N %d X %d Y %d\n",
  536. ppu->scanline,
  537. ppu->control & ppu_Control_Nametable_Mask,
  538. ppu->scroll_x, ppu->scroll_y);
  539. int line = ppu->scanline - (int)nes_ppu_prerender;
  540. render_scanline(ppu, line, data);
  541. } else {
  542. REND_LOG("Scanline %3d -> Postrender\n", ppu->scanline);
  543. SDL_RenderClear(data->renderer);
  544. SDL_UnlockTexture(data->texture);
  545. SDL_RenderCopy(data->renderer, data->texture,
  546. NULL, &data->view);
  547. SDL_LockTextureToSurface(data->texture, NULL,
  548. &data->target);
  549. if (rend->flags & (1 << State_Bit_CRT_Effect)) {
  550. effect_apply(data->effect, data->renderer,
  551. &data->view);
  552. }
  553. sdl_overlay_frame(
  554. &rend->overlay, &data->font, data->renderer,
  555. data->view.x, data->view.y,
  556. data->view.w / nes_ppu_render_w,
  557. data->view.h / nes_ppu_render_h
  558. );
  559. SDL_RenderPresent(data->renderer);
  560. status = 1;
  561. }
  562. return status;
  563. }
  564. static void sdl_redraw_frame(nes_Renderer* rend, int dim) {
  565. sdl_render_data* data = (sdl_render_data*)rend->data;
  566. if (dim) {
  567. SDL_SetTextureAlphaMod(data->texture, 0x7F);
  568. SDL_SetTextureBlendMode(data->texture,
  569. SDL_BLENDMODE_BLEND);
  570. }
  571. SDL_RenderClear(data->renderer);
  572. SDL_UnlockTexture(data->texture);
  573. SDL_RenderCopy(data->renderer, data->texture,
  574. NULL, &data->view);
  575. SDL_LockTextureToSurface(data->texture, NULL, &data->target);
  576. if (rend->flags & (1 << State_Bit_CRT_Effect)) {
  577. effect_apply(data->effect, data->renderer, &data->view);
  578. }
  579. if (dim) {
  580. SDL_SetTextureBlendMode(data->texture,
  581. SDL_BLENDMODE_NONE);
  582. }
  583. }
  584. static void sdl_draw_present(nes_Renderer* rend) {
  585. sdl_render_data* data = (sdl_render_data*)rend->data;
  586. SDL_RenderPresent(data->renderer);
  587. }
  588. static void sdl_draw_text(nes_Renderer* rend,
  589. const char* str, int x, int y,
  590. uint32_t color) {
  591. sdl_render_data* data = (sdl_render_data*)rend->data;
  592. render_string(data->renderer,
  593. x, y, data->view.x, data->view.y,
  594. data->view.w / nes_ppu_render_w,
  595. data->view.h / nes_ppu_render_h,
  596. &data->font, str, color);
  597. }
  598. static void sdl_text_size(nes_Renderer* rend,
  599. const char* str, int* w, int* h) {
  600. sdl_render_data* data = (sdl_render_data*)rend->data;
  601. measure_string(&data->font, str, w, h);
  602. }
  603. nes_Renderer sdl_renderer = {
  604. .init = sdl_render_init,
  605. .done = sdl_render_done,
  606. .render = sdl_render,
  607. .set_flags = sdl_render_set_flags,
  608. .refresh = sdl_render_refresh,
  609. .draw_last_frame = sdl_redraw_frame,
  610. .draw_text = sdl_draw_text,
  611. .text_size = sdl_text_size,
  612. .draw_done = sdl_draw_present,
  613. };