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.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

917 rindas
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. };