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.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

917 行
28KB

  1. #include <SDL2/SDL.h>
  2. #include "render.h"
  3. #include "ppu.h"
  4. #include "mapper.h"
  5. static SDL_Color nes_palette[64] = {
  6. {0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6},
  7. {0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00},
  8. {0x7B,0x2B,0x00}, {0x00,0x3E,0x00}, {0x00,0x48,0x0D}, {0x00,0x3C,0x22},
  9. {0x00,0x2F,0x66}, {0x00,0x00,0x00}, {0x05,0x05,0x05}, {0x05,0x05,0x05},
  10. {0xC8,0xC8,0xC8}, {0x00,0x59,0xFF}, {0x44,0x3C,0xFF}, {0xB7,0x33,0xCC},
  11. {0xFF,0x33,0xAA}, {0xFF,0x37,0x5E}, {0xFF,0x37,0x1A}, {0xD5,0x4B,0x00},
  12. {0xC4,0x62,0x00}, {0x3C,0x7B,0x00}, {0x1E,0x84,0x15}, {0x00,0x95,0x66},
  13. {0x00,0x84,0xC4}, {0x11,0x11,0x11}, {0x09,0x09,0x09}, {0x09,0x09,0x09},
  14. {0xFF,0xFF,0xFF}, {0x00,0x95,0xFF}, {0x6F,0x84,0xFF}, {0xD5,0x6F,0xFF},
  15. {0xFF,0x77,0xCC}, {0xFF,0x6F,0x99}, {0xFF,0x7B,0x59}, {0xFF,0x91,0x5F},
  16. {0xFF,0xA2,0x33}, {0xA6,0xBF,0x00}, {0x51,0xD9,0x6A}, {0x4D,0xD5,0xAE},
  17. {0x00,0xD9,0xFF}, {0x66,0x66,0x66}, {0x0D,0x0D,0x0D}, {0x0D,0x0D,0x0D},
  18. {0xFF,0xFF,0xFF}, {0x84,0xBF,0xFF}, {0xBB,0xBB,0xFF}, {0xD0,0xBB,0xFF},
  19. {0xFF,0xBF,0xEA}, {0xFF,0xBF,0xCC}, {0xFF,0xC4,0xB7}, {0xFF,0xCC,0xAE},
  20. {0xFF,0xD9,0xA2}, {0xCC,0xE1,0x99}, {0xAE,0xEE,0xB7}, {0xAA,0xF7,0xEE},
  21. {0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11}
  22. };
  23. static inline uint8_t* chr_mem(const nes_ppu* ppu,
  24. uint16_t addr) {
  25. return ppu->mapper->chr_addr(ppu->mapper, addr);
  26. }
  27. typedef struct {
  28. SDL_Window* window;
  29. SDL_Renderer* renderer;
  30. SDL_Surface* background;
  31. SDL_Surface* background_line;
  32. SDL_Surface* foreground;
  33. SDL_Surface* sprite;
  34. SDL_Surface* target;
  35. } sdl_render_data;
  36. static sdl_render_data the_render_data = {0};
  37. static int sdl_render_init(nes_Renderer* rend) {
  38. sdl_render_data* data = &the_render_data;
  39. int status = SDL_Init(SDL_INIT_VIDEO);
  40. if (0 != status) {
  41. fprintf(stderr, "SDL: Failed to initialize\n");
  42. } else {
  43. data->window = SDL_CreateWindow(
  44. "NESe",
  45. SDL_WINDOWPOS_UNDEFINED,
  46. SDL_WINDOWPOS_UNDEFINED,
  47. nes_ppu_scan_w * 4,
  48. nes_ppu_scan_h * 4,
  49. 0
  50. );
  51. if (NULL == data->window) {
  52. fprintf(stderr, "SDL: Failed to create window\n");
  53. SDL_Quit();
  54. status = -1;
  55. }
  56. }
  57. if (0 == status) {
  58. data->renderer = SDL_CreateRenderer(data->window, -1, 0);
  59. if (NULL == data->renderer) {
  60. fprintf(stderr, "SDL: Failed to create renderer\n");
  61. SDL_DestroyWindow(data->window);
  62. SDL_Quit();
  63. status = -1;
  64. }
  65. }
  66. if (0 == status) {
  67. data->background = SDL_CreateRGBSurfaceWithFormat(
  68. 0, nes_ppu_render_w, nes_ppu_render_h,
  69. 8, SDL_PIXELFORMAT_INDEX8
  70. );
  71. if (NULL == data->background) {
  72. fprintf(stderr, "SDL: Failed to create background\n");
  73. SDL_DestroyRenderer(data->renderer);
  74. SDL_DestroyWindow(data->window);
  75. SDL_Quit();
  76. status = -1;
  77. }
  78. }
  79. if (0 == status) {
  80. data->background_line = SDL_CreateRGBSurfaceWithFormat(
  81. 0, nes_ppu_render_w + 8, 8,
  82. 8, SDL_PIXELFORMAT_INDEX8
  83. );
  84. if (NULL == data->background_line) {
  85. fprintf(stderr, "SDL: Failed to create block buffer\n");
  86. SDL_FreeSurface(data->background);
  87. SDL_DestroyRenderer(data->renderer);
  88. SDL_DestroyWindow(data->window);
  89. SDL_Quit();
  90. status = -1;
  91. }
  92. }
  93. if (0 == status) {
  94. data->foreground = SDL_CreateRGBSurfaceWithFormat(
  95. 0, nes_ppu_render_w, nes_ppu_render_h,
  96. 8, SDL_PIXELFORMAT_INDEX8
  97. );
  98. if (NULL == data->foreground) {
  99. fprintf(stderr, "SDL: Failed to create foreground\n");
  100. SDL_FreeSurface(data->background_line);
  101. SDL_FreeSurface(data->background);
  102. SDL_DestroyRenderer(data->renderer);
  103. SDL_DestroyWindow(data->window);
  104. SDL_Quit();
  105. status = -1;
  106. }
  107. }
  108. if (0 == status) {
  109. data->sprite = SDL_CreateRGBSurfaceWithFormat(
  110. 0, 8, 16, 8, SDL_PIXELFORMAT_INDEX8
  111. );
  112. if (NULL == data->sprite) {
  113. fprintf(stderr, "SDL: Failed to create sprite\n");
  114. SDL_FreeSurface(data->foreground);
  115. SDL_FreeSurface(data->background_line);
  116. SDL_FreeSurface(data->background);
  117. SDL_DestroyRenderer(data->renderer);
  118. SDL_DestroyWindow(data->window);
  119. SDL_Quit();
  120. status = -1;
  121. }
  122. }
  123. if (0 == status) {
  124. data->target = SDL_CreateRGBSurfaceWithFormat(
  125. 0U, nes_ppu_render_w, nes_ppu_render_h,
  126. 24U, SDL_PIXELFORMAT_RGB888
  127. );
  128. if (NULL == data->target) {
  129. fprintf(stderr, "SDL: Failed to create target\n");
  130. SDL_FreeSurface(data->sprite);
  131. SDL_FreeSurface(data->foreground);
  132. SDL_FreeSurface(data->background_line);
  133. SDL_FreeSurface(data->background);
  134. SDL_DestroyRenderer(data->renderer);
  135. SDL_DestroyWindow(data->window);
  136. SDL_Quit();
  137. status = -1;
  138. }
  139. }
  140. if (0 == status) {
  141. SDL_SetPaletteColors(data->background->format->palette,
  142. nes_palette, 0U, 64U);
  143. SDL_SetColorKey(data->background, SDL_TRUE, 0xFFU);
  144. SDL_SetPaletteColors(data->foreground->format->palette,
  145. nes_palette, 0U, 64U);
  146. SDL_SetColorKey(data->foreground, SDL_TRUE, 0xFFU);
  147. SDL_SetPaletteColors(data->sprite->format->palette,
  148. nes_palette, 0U, 64U);
  149. SDL_SetColorKey(data->sprite, SDL_TRUE, 0xFFU);
  150. rend->data = &the_render_data;
  151. }
  152. return status;
  153. }
  154. static void sdl_render_done(nes_Renderer* rend) {
  155. sdl_render_data* data = (sdl_render_data*)rend->data;
  156. SDL_FreeSurface(data->target);
  157. SDL_FreeSurface(data->sprite);
  158. SDL_FreeSurface(data->foreground);
  159. SDL_FreeSurface(data->background_line);
  160. SDL_FreeSurface(data->background);
  161. SDL_DestroyRenderer(data->renderer);
  162. SDL_DestroyWindow(data->window);
  163. SDL_Quit();
  164. }
  165. static inline void render_sprite_line(
  166. const nes_ppu* ppu, int index, int y, const uint8_t* pal,
  167. uint8_t* dst, int start, int end) {
  168. uint8_t* sprite = chr_mem(ppu, index * 16U);
  169. uint8_t lo = sprite[0U + y] << start;
  170. uint8_t hi = sprite[8U + y] << start;
  171. for (int x = start; x < end; ++x) {
  172. int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7));
  173. if (pal_idx) *dst = pal[pal_idx];
  174. dst++;
  175. hi <<= 1;
  176. lo <<= 1;
  177. }
  178. }
  179. static inline void render_sprite_line_flip(
  180. const nes_ppu* ppu, int index, int y, const uint8_t* pal,
  181. uint8_t* dst, int start, int end) {
  182. uint8_t* sprite = chr_mem(ppu, index * 16U);
  183. uint8_t lo = sprite[0U + y] >> start;
  184. uint8_t hi = sprite[8U + y] >> start;
  185. for (int x = start; x < end; ++x) {
  186. int pal_idx = (((hi & 1) << 1) | (lo & 1));
  187. if (pal_idx) *dst = pal[pal_idx];
  188. dst++;
  189. hi >>= 1;
  190. lo >>= 1;
  191. }
  192. }
  193. static inline void render_bg_sprite_line(
  194. const nes_ppu* ppu, int index, int y, const uint8_t* pal,
  195. uint8_t* dst, int start, int end) {
  196. uint8_t* sprite = chr_mem(ppu, index * 16U);
  197. uint8_t lo = sprite[0U + y] << (start % 8);
  198. uint8_t hi = sprite[8U + y] << (start % 8);
  199. for (int x = start; x < end; ++x) {
  200. int pal_idx = (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7));
  201. *dst = (pal_idx ? pal[pal_idx] : 0xFFU);
  202. dst++;
  203. hi <<= 1;
  204. lo <<= 1;
  205. }
  206. }
  207. /*
  208. static void render_bg_sprite(const nes_ppu* ppu, int index,
  209. const uint8_t* pal,
  210. void* loc, int pitch) {
  211. uint8_t* sprite = chr_mem(ppu, index * 16U);
  212. uint8_t* dst_line = (uint8_t*)loc;
  213. for (int y = 8; y > 0; --y) {
  214. uint8_t lo = sprite[0U];
  215. uint8_t hi = sprite[8U];
  216. uint8_t* dst = dst_line;
  217. for (int x = 8; x > 0; --x) {
  218. int pal_idx = ( ((hi & 0x80) >> 6) |
  219. ((lo & 0x80) >> 7));
  220. *dst++ = (pal_idx ? pal[pal_idx] : 0xFFU);
  221. hi <<= 1;
  222. lo <<= 1;
  223. }
  224. dst_line += pitch;
  225. ++sprite;
  226. }
  227. }
  228. static void render_background_area(const nes_ppu* ppu, int page,
  229. void* buffer, int pitch,
  230. int xs, int ys, int w, int h) {
  231. int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0;
  232. const uint8_t* index_line = nes_map_vram_addr(ppu->mapper,
  233. page << 10);
  234. const uint8_t* attrs = index_line + 960U;
  235. index_line += xs + (ys * nes_ppu_blocks_w);
  236. uint8_t* dst_line = (uint8_t*)buffer;
  237. for (int y = ys; y < h + ys; ++y) {
  238. uint8_t* dst = dst_line;
  239. const uint8_t* index = index_line;
  240. for (int x = xs; x < w + xs; ++x) {
  241. int attr_idx = ((y / 4) * 8) + (x / 4);
  242. int shift = 2 * ((y & 0b10) | ((x & 0b10) >> 1));
  243. int pal_idx = (attrs[attr_idx] >> shift) & 3;
  244. const uint8_t* pal = &ppu->palette[pal_idx * 4];
  245. render_bg_sprite(ppu, bank + *index, pal,
  246. dst, pitch);
  247. ++index;
  248. dst += 8;
  249. }
  250. dst_line += pitch * 8;
  251. index_line += nes_ppu_blocks_w;
  252. }
  253. }
  254. */
  255. static inline void render_bg_scanline_area(
  256. const nes_ppu* ppu, int page,
  257. uint8_t* dst, int x, int y, int w) {
  258. int block_x = x / 8;
  259. int line_end = x + w;
  260. int block_y = y / 8;
  261. y = y % 8;
  262. int bank = (ppu->control & ppu_Control_Back_Bank) ? 0x100 : 0;
  263. const uint8_t* indexes = nes_map_vram_addr(ppu->mapper,
  264. page << 10);
  265. const uint8_t* attrs = indexes + 960U;
  266. const uint8_t* index = indexes +
  267. (block_y * nes_ppu_blocks_w) +
  268. block_x;
  269. while (x < line_end) {
  270. int attr_idx = ((block_y / 4) * 8) + (block_x / 4);
  271. int shift = 2 * ((block_y & 0b10) |
  272. ((block_x & 0b10) >> 1));
  273. int pal_idx = (attrs[attr_idx] >> shift) & 3;
  274. const uint8_t* pal = &ppu->palette[pal_idx * 4];
  275. int end = (x + 8) & ~7;
  276. if (end > line_end) end = line_end;
  277. render_bg_sprite_line(ppu, bank + *index, y,
  278. pal, dst, x, end);
  279. ++index;
  280. ++block_x;
  281. dst += (end - x);
  282. x = end;
  283. }
  284. }
  285. static void render_bg_scanline(const nes_ppu* ppu,/* int scanline,*/
  286. uint8_t* dst) {
  287. int page = (ppu->control & ppu_Control_Nametable_Mask);
  288. int x = ppu->scroll_x;
  289. int y = ppu->scroll_y /*+ scanline*/;
  290. /*
  291. if (y >= nes_ppu_render_h) {
  292. y -= nes_ppu_render_h;
  293. page ^= 0b10;
  294. }
  295. */
  296. int w = (nes_ppu_render_w - x);
  297. if (!(ppu->mask & ppu_Mask_Left_Back)) {
  298. // Handle column 0 flag - need to fill with transparency
  299. memset(dst, 0xFFU, 8);
  300. w -= 8;
  301. x += 8;
  302. dst += 8;
  303. }
  304. render_bg_scanline_area(ppu, page, dst, x, y, w);
  305. render_bg_scanline_area(ppu, page ^ 1, dst + w, 0, y,
  306. nes_ppu_render_w - w);
  307. }
  308. /*
  309. static void render_background_line(const nes_ppu* ppu, int line,
  310. void* buffer, int pitch) {
  311. // TODO: Handle column 0 flag
  312. int page = (ppu->control & ppu_Control_Nametable_Mask);
  313. int x = ppu->scroll_x / 8;
  314. line += ppu->scroll_y / 8;
  315. if (line >= nes_ppu_blocks_h) {
  316. line -= nes_ppu_blocks_h;
  317. page ^= 0b10;
  318. }
  319. // Left
  320. render_background_area(
  321. ppu, page, buffer, pitch,
  322. x, line,
  323. nes_ppu_blocks_w - x, 1
  324. );
  325. // Right
  326. buffer += (nes_ppu_blocks_w - x) * 8U;
  327. render_background_area(
  328. ppu, page ^ 1, buffer, pitch,
  329. 0, line,
  330. 1U + x, 1
  331. );
  332. }
  333. static void render_sprite(nes_ppu* ppu, int index,
  334. const uint8_t* pal, uint8_t attr,
  335. void* loc, int pitch) {
  336. uint8_t* sprite = chr_mem(ppu, index * 16U);
  337. uint8_t* dst_line = (uint8_t*)loc;
  338. int dx = 1;
  339. if (attr & oam_Attr_Flip_X) {
  340. dst_line += 7;
  341. dx = -dx;
  342. }
  343. if (attr & oam_Attr_Flip_Y) {
  344. dst_line += (7 * pitch);
  345. pitch = -pitch;
  346. }
  347. for (int y = 8; y > 0; --y) {
  348. uint8_t lo = sprite[0U];
  349. uint8_t hi = sprite[8U];
  350. uint8_t* dst = dst_line;
  351. for (int x = 8; x > 0; --x) {
  352. int pal_idx = ( ((hi & 0x80) >> 6) |
  353. ((lo & 0x80) >> 7));
  354. int nes_pal_idx = (pal_idx ? pal[pal_idx] : 0xFFU);
  355. *dst = nes_pal_idx;
  356. dst += dx;
  357. hi <<= 1;
  358. lo <<= 1;
  359. }
  360. dst_line += pitch;
  361. ++sprite;
  362. }
  363. }
  364. */
  365. static void render_line_sprites(nes_ppu* ppu, uint8_t* dst_line,
  366. int scanline, int background,
  367. const oam_sprite* sprites,
  368. int n_sprites) {
  369. for (int i_sprite = n_sprites - 1; i_sprite >= 0; --i_sprite) {
  370. const oam_sprite* sprite = &sprites[i_sprite];
  371. if ((sprite->attr & oam_Attr_Background) ^ background) {
  372. continue;
  373. }
  374. int index = sprite->index;
  375. int bank = (ppu->control & ppu_Control_Sprite_Bank) ?
  376. 0x100 : 0;
  377. if (ppu->control & ppu_Control_Sprite_Size) {
  378. bank = (index & 1) ? 0x100 : 0;
  379. index &= 0xFEU;
  380. }
  381. index += bank;
  382. int y = scanline - (sprite->y + 1);
  383. if (ppu->control & ppu_Control_Sprite_Size) {
  384. if (y >= 8) {
  385. index ^= 1;
  386. y -= 8;
  387. }
  388. if (sprite->attr & oam_Attr_Flip_Y) {
  389. index ^= 1;
  390. }
  391. }
  392. int pal_idx = (sprite->attr & oam_Attr_Pal_Mask);
  393. const uint8_t* pal = &ppu->palette[16 + (pal_idx * 4)];
  394. if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y;
  395. int end = nes_ppu_render_w - sprite->x;
  396. if (end > 8) end = 8;
  397. int start = 0;
  398. if ( !(ppu->mask & ppu_Mask_Left_Sprite) &&
  399. sprite->x < 8) {
  400. start = 8 - sprite->x;
  401. }
  402. if (sprite->attr & oam_Attr_Flip_X) {
  403. render_sprite_line_flip(ppu, index, y, pal,
  404. dst_line + sprite->x + start,
  405. start, end);
  406. } else {
  407. render_sprite_line(ppu, index, y, pal,
  408. dst_line + sprite->x + start,
  409. start, end);
  410. }
  411. }
  412. }
  413. /*
  414. static void render_sprites(nes_ppu* ppu,
  415. SDL_Surface* buffer,
  416. SDL_Surface* target,
  417. int background) {
  418. int bank = (ppu->control & ppu_Control_Sprite_Bank) ?
  419. 0x100 : 0;
  420. const oam_sprite* sprites = (const oam_sprite*)ppu->oam;
  421. uint8_t* dst_origin = (uint8_t*)buffer->pixels;
  422. int pitch = buffer->pitch;
  423. for ( int i_sprite = nes_ppu_oam_sprite_count - 1;
  424. i_sprite >= 0; --i_sprite) {
  425. const oam_sprite* sprite = &sprites[i_sprite];
  426. if ((sprite->attr & oam_Attr_Background) ^ background) {
  427. continue;
  428. }
  429. if ( !(ppu->mask & ppu_Mask_Left_Sprite) &&
  430. sprite->x < 8) {
  431. continue;
  432. }
  433. int y = (sprite->y + 1);
  434. if (y >= nes_ppu_render_h) continue;
  435. uint8_t* dst = dst_origin;
  436. int index = sprite->index;
  437. if (ppu->control & ppu_Control_Sprite_Size) {
  438. bank = (index & 1) ? 0x100 : 0;
  439. index &= 0xFEU;
  440. }
  441. index += bank;
  442. int pal_idx = (sprite->attr & oam_Attr_Pal_Mask);
  443. const uint8_t* pal = &ppu->palette[16 + (pal_idx * 4)];
  444. if (ppu->control & ppu_Control_Sprite_Size) {
  445. if (sprite->attr & oam_Attr_Flip_Y) index++;
  446. render_sprite(ppu, index, pal, sprite->attr,
  447. dst, pitch);
  448. dst += pitch * 8;
  449. if (sprite->attr & oam_Attr_Flip_Y) {
  450. index--;
  451. } else {
  452. index++;
  453. }
  454. }
  455. render_sprite(ppu, index, pal, sprite->attr,
  456. dst, pitch);
  457. SDL_Rect sprite_rect = {
  458. .x = 0,
  459. .y = 0,
  460. .w = 8,
  461. .h = (ppu->control & ppu_Control_Sprite_Size) ?
  462. 16 : 8,
  463. };
  464. SDL_Rect target_rect = {
  465. .x = sprite->x,
  466. .y = y,
  467. .w = 8,
  468. .h = (ppu->control & ppu_Control_Sprite_Size) ?
  469. 16 : 8,
  470. };
  471. SDL_BlitSurface(buffer, &sprite_rect,
  472. target, &target_rect);
  473. }
  474. }
  475. */
  476. // Check sprite (0 only) collision on a scanline
  477. // This assumes that we've verified that this sprite
  478. // intersects with this scanline.
  479. // Scanline is 0-239 from inside the rendering window
  480. // (though we should never see this called with 0).
  481. static int eval_sprite_line(const nes_ppu* ppu, int y,
  482. const oam_sprite* sprite,
  483. const uint8_t* chr,
  484. const uint8_t* back) {
  485. int hit_pos = -1;
  486. if (sprite->attr & oam_Attr_Flip_Y) y = 7 - y;
  487. uint8_t lo = chr[0U + y];
  488. uint8_t hi = chr[8U + y];
  489. back += sprite->x;
  490. int end = nes_ppu_render_w;
  491. if (end > sprite->x + 8) end = sprite->x + 8;
  492. for (int x = sprite->x; x < end; ++x) {
  493. int pal_idx = (sprite->attr & oam_Attr_Flip_X) ?
  494. (((hi & 1) << 1) | (lo & 1)) :
  495. (((hi & 0x80) >> 6) | ((lo & 0x80) >> 7));
  496. if (pal_idx && *back != 0xFFU) {
  497. hit_pos = x;
  498. break;
  499. }
  500. ++back;
  501. if (sprite->attr & oam_Attr_Flip_X) {
  502. hi >>= 1;
  503. lo >>= 1;
  504. } else {
  505. hi <<= 1;
  506. lo <<= 1;
  507. }
  508. }
  509. return hit_pos;
  510. }
  511. #if 0
  512. static void update_sprite_hit(nes_ppu* ppu, int block_line,
  513. const void* back_pixels,
  514. int back_pitch) {
  515. const oam_sprite* sprite = (oam_sprite*)ppu->oam;
  516. int x_fine = ppu->scroll_x % 8;
  517. int y_fine = ppu->scroll_x % 8;
  518. int index = sprite->index;
  519. if (ppu->control & ppu_Control_Sprite_Bank) {
  520. index += 0x100U;
  521. }
  522. const uint8_t* chr = chr_mem(ppu, index * 16U);
  523. int render_line = block_line * 8U;
  524. int start_y = (sprite->y + 1) + y_fine - render_line;
  525. int end_y = start_y + 8;
  526. if (start_y < 8 && end_y > 0) {
  527. if (start_y < 0) start_y = 0;
  528. if (end_y > 8) end_y = 8;
  529. int hit = -1;
  530. const uint8_t* back = (uint8_t*)back_pixels + x_fine;
  531. back += (render_line + start_y) * back_pitch;
  532. for (int y = start_y; y < end_y; ++y) {
  533. hit = eval_sprite_line(
  534. ppu, render_line + y - y_fine,
  535. sprite, chr, back
  536. );
  537. if (hit >= 0) {
  538. ppu->hit_line = y - y_fine + render_line;
  539. ppu->hit_dot = hit;
  540. break;
  541. }
  542. back += back_pitch;
  543. }
  544. }
  545. }
  546. static void render_block_line(nes_ppu* ppu, int line,
  547. sdl_render_data* data) {
  548. // Render single line of blocks
  549. render_background_line(ppu, line,
  550. data->background_line->pixels,
  551. data->background_line->pitch);
  552. int x_fine = ppu->scroll_x % 8;
  553. int y_fine = ppu->scroll_y % 8;
  554. // Check for Sprite 0 Hit
  555. if ( 0 >= ppu->hit_line &&
  556. (ppu->mask & ppu_Mask_Sprite)) {
  557. update_sprite_hit(ppu, line,
  558. data->background_line->pixels,
  559. data->background_line->pitch);
  560. }
  561. // Copy line onto full background
  562. const uint8_t* src = data->background_line->pixels +
  563. x_fine;
  564. int start_y = ((line * 8U) - y_fine);
  565. int end_y = start_y + 8;
  566. if (start_y < 0) {
  567. src -= (start_y * data->background_line->pitch);
  568. start_y = 0;
  569. }
  570. uint8_t* dst = data->background->pixels +
  571. (start_y * data->background->pitch);
  572. for (int y = start_y; y < end_y; ++y) {
  573. if ((void*)dst >= data->background->pixels) {
  574. memcpy(dst, src, nes_ppu_render_w);
  575. }
  576. src += data->background_line->pitch;
  577. dst += data->background->pitch;
  578. }
  579. /*
  580. SDL_Rect back_rect = {
  581. .x = x_fine,
  582. .y = 0,
  583. .w = nes_ppu_render_w,
  584. .h = 8U,
  585. };
  586. SDL_Rect render_rect = {
  587. .x = 0,
  588. .y = (line * 8U) - y_fine,
  589. .w = nes_ppu_render_w,
  590. .h = 8U,
  591. };
  592. SDL_BlitSurface(data->background_line, &back_rect,
  593. data->background, &render_rect);
  594. */
  595. }
  596. #endif
  597. static void update_scanline_hit(nes_ppu* ppu, uint8_t* back_line,
  598. int scanline) {
  599. const oam_sprite* sprite = &ppu->oam[0];
  600. int bank = (ppu->control & ppu_Control_Sprite_Bank) ?
  601. 0x100 : 0;
  602. if ( !(ppu->mask & ppu_Mask_Left_Sprite) &&
  603. sprite->x < 8) {
  604. return;
  605. }
  606. int y_pos = (sprite->y + 1);
  607. int y = scanline - y_pos;
  608. if (0 > y) return;
  609. int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8;
  610. if (y >= h) return;
  611. int index = sprite->index;
  612. if (ppu->control & ppu_Control_Sprite_Size) {
  613. bank = (index & 1) ? 0x100 : 0;
  614. index &= 0xFEU;
  615. }
  616. index += bank;
  617. if (ppu->control & ppu_Control_Sprite_Size) {
  618. if (y >= 8) {
  619. index ^= 1;
  620. y -= 8;
  621. }
  622. if (sprite->attr & oam_Attr_Flip_Y) {
  623. index ^= 1;
  624. }
  625. }
  626. const uint8_t* chr = chr_mem(ppu, index * 16U);
  627. int hit = eval_sprite_line(ppu, y, sprite, chr, back_line);
  628. if (hit >= 0) {
  629. REND_LOG("Upcoming hit @ %d, %d\n", scanline + 1, hit);
  630. REND_LOG("(Currently @ %d, %d)\n", ppu->scanline, ppu->cycle);
  631. ppu->hit_line = scanline;
  632. ppu->hit_dot = hit;
  633. }
  634. }
  635. static int select_line_sprites(const nes_ppu* ppu, int scanline,
  636. oam_sprite* sprites, int max) {
  637. int n_sprites = 0;
  638. for ( int i_sprite = 0;
  639. i_sprite < nes_ppu_oam_sprite_count && n_sprites < max;
  640. ++i_sprite) {
  641. const oam_sprite* sprite = &ppu->oam[i_sprite];
  642. int y_pos = (sprite->y + 1);
  643. int y = scanline - y_pos;
  644. if (0 > y) continue;
  645. int h = (ppu->control & ppu_Control_Sprite_Size) ? 16 : 8;
  646. if (y >= h) continue;
  647. *sprites = *sprite;
  648. ++sprites;
  649. ++n_sprites;
  650. }
  651. return n_sprites;
  652. }
  653. static void render_scanline(nes_ppu* ppu, int line,
  654. sdl_render_data* data) {
  655. SDL_Rect dst_rect = {
  656. .x = 0,
  657. .y = line,
  658. .w = nes_ppu_render_w,
  659. .h = 1,
  660. };
  661. if (line >= 0) {
  662. // Emulate the happy part of the backdrop override quirk
  663. int pal_idx = (ppu->addr >= nes_ppu_mem_pal_start) ?
  664. (ppu->addr & (nes_ppu_mem_pal_size - 1)) : 0;
  665. SDL_Color ext = nes_palette[ppu->palette[pal_idx]];
  666. SDL_FillRect(data->target, &dst_rect, ((int)ext.r << 16) |
  667. ((int)ext.g << 8) |
  668. ext.b);
  669. }
  670. if (!(ppu->mask & (ppu_Mask_Sprite | ppu_Mask_Back))) {
  671. // Do nothing if BOTH are disabled.
  672. return;
  673. }
  674. SDL_Rect src_rect = {
  675. .x = 0,
  676. .y = 0,
  677. .w = nes_ppu_render_w,
  678. .h = 1,
  679. };
  680. uint8_t* foreground = data->foreground->pixels;
  681. uint8_t* background = data->background->pixels;
  682. // We check for hits if EITHER layer is enabled.
  683. render_bg_scanline(ppu, background);
  684. if (ppu->hit_line <= 0) {
  685. update_scanline_hit(ppu, background, line);
  686. }
  687. if (line >= 0) {
  688. oam_sprite line_sprites[8] = {0};
  689. int n_sprites = select_line_sprites(ppu, line,
  690. line_sprites, 8);
  691. if (ppu->mask & ppu_Mask_Sprite) {
  692. memset(foreground, 0xFFU, nes_ppu_render_w);
  693. render_line_sprites(ppu, foreground, line,
  694. oam_Attr_Background,
  695. line_sprites, n_sprites);
  696. SDL_BlitSurface(data->foreground, &src_rect,
  697. data->target, &dst_rect);
  698. }
  699. if (ppu->mask & ppu_Mask_Back) {
  700. SDL_BlitSurface(data->background, &src_rect,
  701. data->target, &dst_rect);
  702. }
  703. if (ppu->mask & ppu_Mask_Sprite) {
  704. memset(foreground, 0xFFU, nes_ppu_render_w);
  705. render_line_sprites(ppu, foreground, line, 0,
  706. line_sprites, n_sprites);
  707. SDL_BlitSurface(data->foreground, &src_rect,
  708. data->target, &dst_rect);
  709. }
  710. }
  711. /*if (line + 1 < nes_ppu_height)*/ {
  712. ppu->scroll_y++;
  713. if (ppu->scroll_y >= nes_ppu_render_h) {
  714. ppu->scroll_y -= nes_ppu_render_h;
  715. ppu->control ^= 0b10;
  716. }
  717. /*
  718. // We check for hits if EITHER layer is enabled.
  719. render_bg_scanline(ppu, background);
  720. if (ppu->hit_line <= 0) {
  721. update_scanline_hit(ppu, background, line + 1);
  722. }
  723. */
  724. }
  725. }
  726. static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) {
  727. int status = 0;
  728. sdl_render_data* data = (sdl_render_data*)rend->data;
  729. if (ppu->scanline < nes_ppu_prerender) {
  730. REND_LOG("Scanline %3d -> Prerender\n", ppu->scanline);
  731. /*
  732. memset(data->foreground->pixels, 0xFFU,
  733. data->foreground->pitch * data->foreground->h);
  734. if (ppu->mask & ppu_Mask_Sprite) {
  735. render_sprites(ppu, data->sprite, data->target,
  736. oam_Attr_Background);
  737. render_sprites(ppu, data->sprite, data->foreground, 0);
  738. }
  739. */
  740. /*
  741. int line = ppu->scanline - (int)nes_ppu_prerender;
  742. render_scanline(ppu, line, data);
  743. */
  744. } else if (ppu->scanline < nes_ppu_prerender +
  745. nes_ppu_height) {
  746. /*
  747. int line = (ppu->scanline - (int)nes_ppu_prerender) / 8;
  748. REND_LOG("Scanline %3d -> Line %2d @ X %d\n", ppu->scanline, line, ppu->scroll_x);
  749. // TODO: Only re-render if VRAM/scroll changes?
  750. if (ppu->mask & ppu_Mask_Back) {
  751. render_block_line(ppu, line, data);
  752. }
  753. */
  754. REND_LOG("Scanline %3d : N %d X %d Y %d\n",
  755. ppu->scanline,
  756. ppu->control & ppu_Control_Nametable_Mask,
  757. ppu->scroll_x, ppu->scroll_y);
  758. int line = ppu->scanline - (int)nes_ppu_prerender;
  759. render_scanline(ppu, line, data);
  760. } else {
  761. REND_LOG("Scanline %3d -> Postrender\n", ppu->scanline);
  762. /*
  763. if (ppu->mask & ppu_Mask_Sprite) {
  764. render_sprites(ppu, data->sprite, data->target,
  765. oam_Attr_Background);
  766. }
  767. */
  768. /*
  769. if (ppu->mask & ppu_Mask_Back) {
  770. // Render final partial line
  771. if (0 != (ppu->scroll_y % 8)) {
  772. if (ppu->mask & ppu_Mask_Sprite) {
  773. // TODO: Render background sprites that start this block
  774. }
  775. if (ppu->mask & ppu_Mask_Back) {
  776. render_block_line(ppu, nes_ppu_blocks_h, data);
  777. }
  778. if (ppu->mask & ppu_Mask_Sprite) {
  779. // TODO: Render foreground sprites that end this block
  780. }
  781. }
  782. SDL_BlitSurface(data->background, NULL,
  783. data->target, NULL);
  784. }
  785. if (ppu->mask & ppu_Mask_Sprite) {
  786. render_sprites(ppu, data->sprite, data->target, 0);
  787. SDL_BlitSurface(data->foreground, NULL,
  788. data->target, NULL);
  789. }
  790. */
  791. SDL_Texture* texture = SDL_CreateTextureFromSurface(
  792. data->renderer, data->target
  793. );
  794. SDL_RenderCopy(data->renderer, texture, NULL, NULL);
  795. SDL_RenderPresent(data->renderer);
  796. SDL_DestroyTexture(texture);
  797. status = 1;
  798. }
  799. return status;
  800. }
  801. nes_Renderer sdl_renderer = {
  802. .init = sdl_render_init,
  803. .done = sdl_render_done,
  804. .render = sdl_render,
  805. };