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.
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.

723 line
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. };