Advertisement
Guest User

Untitled

a guest
Jul 19th, 2019
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.05 KB | None | 0 0
  1. #pragma once
  2.  
  3. #include "cartridge.h"
  4. #include "display.h"
  5. #include "nmi_flipflop.h"
  6.  
  7. #include <array>
  8.  
  9. #define NTH_BIT(x, n) (((x) >> (n)) & 1)
  10.  
  11. enum ppu_map : uint8_t
  12. {
  13. PPUCTRL = 0x00,
  14. PPUMASK = 0x01,
  15. PPUSTATUS = 0x02,
  16. OAMADDR = 0x03,
  17. OAMDATA = 0x04,
  18. PPUSCROLL = 0x05,
  19. PPUADDR = 0x06,
  20. PPUDATA = 0x07
  21. };
  22.  
  23. constexpr uint32_t nes_palette[] =
  24. {
  25. 0x7C7C7C, 0xFC0000, 0xBC0000, 0xBC2844, 0x840094, 0x2000A8, 0x0010A8, 0x001488, //
  26. 0x003050, 0x007800, 0x006800, 0x005800, 0x584000, 0x000000, 0x000000, 0x000000, //
  27. 0xBCBCBC, 0xF87800, 0xF85800, 0xFC4468, 0xCC00D8, 0x5800E4, 0x0038F8, 0x105CE4, //
  28. 0x007CAC, 0x00B800, 0x00A800, 0x44A800, 0x888800, 0x000000, 0x000000, 0x000000, //
  29. 0xF8F8F8, 0xFCBC3C, 0xFC8868, 0xF87898, 0xF878F8, 0x9858F8, 0x5878F8, 0x44A0FC, //
  30. 0x00B8F8, 0x18F8B8, 0x54D858, 0x98F858, 0xD8E800, 0x787878, 0x000000, 0x000000, //
  31. 0xFCFCFC, 0xFCE4A4, 0xF8B8B8, 0xF8B8D8, 0xF8B8F8, 0xC0A4F8, 0xB0D0F0, 0xA8E0FC, //
  32. 0x78D8F8, 0x78F8D8, 0xB8F8B8, 0xD8F8B8, 0xFCFC00, 0xF8D8F8, 0x000000, 0x000000 //
  33. };
  34.  
  35. namespace PPU
  36. {
  37. struct sprite
  38. {
  39. uint8_t id;
  40. uint8_t x;
  41. uint8_t y;
  42. uint8_t tile;
  43. uint8_t attributes;
  44. uint8_t data_l;
  45. uint8_t data_h;
  46. };
  47.  
  48. nes::cartridge * cartridge;
  49. platform::display * display;
  50. nes::nmi_flipflop * nmi_flipflop;
  51.  
  52. std::array<uint8_t , 0x0800> nametable_ram;
  53. std::array<uint8_t , 0x20> palette_ram;
  54. std::array<uint8_t , 0x0100> oam_ram;
  55. std::array<sprite , 0x08> oam_0;
  56. std::array<sprite , 0x08> oam_1;
  57. std::array<uint32_t, 0xF000> framebuffer;
  58.  
  59. uint16_t
  60. v,
  61. t,
  62. line,
  63. dot,
  64. address_buffer,
  65. background_shift_register_l,
  66. background_shift_register_h;
  67.  
  68. uint8_t
  69. x,
  70. w,
  71. data_latch,
  72. data_buffer,
  73. oam_address,
  74. control_flags,
  75. mask_flags,
  76. status_flags,
  77. name_buffer,
  78. attribute_buffer,
  79. background_tile_l,
  80. background_tile_h,
  81. attribute_shift_register_l,
  82. attribute_shift_register_h,
  83. odd_frame,
  84. attribute_latch_l,
  85. attribute_latch_h;
  86.  
  87. void (*dot_process)();
  88. void dot_0_0();
  89. void dot_240_0();
  90. void dot_nt_odd();
  91. void dot_nt_even();
  92.  
  93. inline bool background_enabled()
  94. {
  95. return !!(0x18 & mask_flags);
  96. }
  97. inline uint16_t nametable_address()
  98. {
  99. return 0x2000 | (v & 0x0FFF);
  100. }
  101. inline uint16_t attributes_address()
  102. {
  103. return (0x23C0)
  104. | (0x0C00 & (v ))
  105. | (0x0038 & (v >> 4))
  106. | (0x0007 & (v >> 2));
  107. }
  108. inline uint16_t background_address()
  109. {
  110. return (0x1000 & (control_flags << 8))
  111. | (0x0FF0 & (name_buffer << 4))
  112. | (0x0007 & (v >> 12));
  113. }
  114. inline uint16_t map_nametable_address(uint16_t address)
  115. {
  116. switch (cartridge->get_mirroring())
  117. {
  118. case nes::mirroring::Vertical:
  119. return 0x07FF & address;
  120. case nes::mirroring::Horizontal:
  121. return (0x0400 & (address >> 1)) + (0x03FF & address);
  122. default:
  123. return address - 0x2000;
  124. }
  125. }
  126. inline uint16_t map_palette_address(uint16_t address)
  127. {
  128. return (address & ((0x0003 & address) ? ~0x0000 : ~0x0010)) & 0x001F;
  129. }
  130. inline uint8_t read_nametable(uint16_t address)
  131. {
  132. return nametable_ram[address];
  133. }
  134. inline void write_nametable(uint16_t address, uint8_t value)
  135. {
  136. nametable_ram[address] = value;
  137. }
  138. inline uint8_t read_palette(uint16_t address)
  139. {
  140. return ((mask_flags & 0x01) ? 0x18 : 0xFF) & palette_ram[address];
  141. }
  142. inline void write_palette(uint16_t address, uint8_t value)
  143. {
  144. palette_ram[address] = value;
  145. }
  146. inline uint8_t read_memory(uint16_t address)
  147. {
  148. if (address & 0xC000) throw std::runtime_error{ std::string{ "Illegal ppu read at " } + std::to_string(address) };
  149. if (~address & 0x3F00)
  150. {
  151. if (address & 0x2000) return read_nametable(map_nametable_address(address));
  152. return cartridge->read_chr(address);
  153. }
  154. return read_palette(map_palette_address(address));
  155. }
  156. inline void write_memory(uint16_t address, uint8_t value)
  157. {
  158. if (address & 0xC000) throw std::runtime_error{ std::string{ "Illegal ppu read at " } + std::to_string(address) };
  159. if (~address & 0x3F00)
  160. {
  161. if (address & 0x2000) return write_nametable(map_nametable_address(address), value);
  162. return cartridge->write_chr(address, value);
  163. }
  164. return write_palette(map_palette_address(address), value);
  165. }
  166. inline void h_scroll()
  167. {
  168. if (!background_enabled()) return;
  169. if ((0x001F & v) == 0x001F) v ^= 0x041F;
  170. else v = (~0x001F & v) | (0x001F & (v + 0x0001));
  171. }
  172. inline void v_scroll()
  173. {
  174. if (!background_enabled()) return;
  175. uint16_t v1 = ~0x7000 & v;
  176. if ((0x7000 & v) == 0x7000)
  177. {
  178. v1 = ~0x03E0 & v1;
  179. if ((0x03E0 & v) == 0x03E0) v = v1;
  180. else if ((0x03E0 & v) == 0x03A0) v = v1 ^ 0x0800;
  181. else v = v1 | (0x03E0 & (v + 0x0020));
  182. }
  183. else v = v1 | (0x7000 & (v + 0x1000));
  184. }
  185. inline void h_update()
  186. {
  187. if (!background_enabled()) return;
  188. v = (~0x041F & v)
  189. | ( 0x041F & t);
  190. }
  191. inline void v_update()
  192. {
  193. if (!background_enabled()) return;
  194. v = (~0x7BE0 & v)
  195. | ( 0x7BE0 & t);
  196. }
  197. inline void reload_shift_registers()
  198. {
  199. background_shift_register_l = (background_shift_register_l & 0xFF00) | background_tile_l;
  200. background_shift_register_h = (background_shift_register_h & 0xFF00) | background_tile_h;
  201. attribute_latch_l = (attribute_buffer & 0x01);
  202. attribute_latch_h = (attribute_buffer & 0x02) >> 1;
  203. }
  204. inline uint8_t sprite_height()
  205. {
  206. return (0x20 & control_flags) ? 0x10 : 0x08;
  207. }
  208. inline void clear_oam_1()
  209. {
  210. for (int i = 0; i < 8; i++)
  211. {
  212. oam_1[i].id = 0x40;
  213. oam_1[i].y = 0xFF;
  214. oam_1[i].tile = 0xFF;
  215. oam_1[i].attributes = 0xFF;
  216. oam_1[i].x = 0xFF;
  217. oam_1[i].data_l = 0x00;
  218. oam_1[i].data_h = 0x00;
  219. }
  220. }
  221. inline void evaluate_sprites()
  222. {
  223. for (int i = 0, n = 0; i < 64; i++)
  224. {
  225. int sprite_line = line - oam_ram[i << 2];
  226. if (sprite_line >= 0 && sprite_line < sprite_height())
  227. {
  228. oam_1[n].id = i;
  229. oam_1[n].y = oam_ram[(i << 2) + 0x0000];
  230. oam_1[n].tile = oam_ram[(i << 2) + 0x0001];
  231. oam_1[n].attributes = oam_ram[(i << 2) + 0x0002];
  232. oam_1[n].x = oam_ram[(i << 2) + 0x0003];
  233. if(n++ == 8)
  234. {
  235. status_flags |= 0x20;
  236. return;
  237. }
  238. }
  239. }
  240. }
  241. inline void load_sprites()
  242. {
  243. for (int i = 0; i < 8; i++)
  244. {
  245. oam_0[i] = oam_1[i];
  246. uint16_t address =
  247. (sprite_height() == 0x10)
  248. ? (0x1000 & (oam_0[i].tile << 12)) | (0x0FE0 & (oam_0[i].tile << 4))
  249. : (0x1000 & (control_flags << 9)) | ( (oam_0[i].tile << 4));
  250.  
  251. uint8_t sprite_line = (line - oam_0[i].y) % sprite_height();
  252. if (oam_0[i].attributes & 0x80) sprite_line ^= sprite_height() - 0x01;
  253. address += sprite_line + (sprite_line & 0x08);
  254. oam_0[i].data_l = read_memory(address + 0);
  255. oam_0[i].data_h = read_memory(address + 8);
  256. }
  257. }
  258. inline void pixel()
  259. {
  260. uint8_t palette = 0;
  261. uint8_t obj_palette = 0;
  262. uint8_t obj_priority = 0;
  263. int16_t pxx = dot - 2;
  264.  
  265. if ((0x08 & mask_flags) && ((0x02 & mask_flags) || pxx >= 8))
  266. {
  267. palette = (NTH_BIT(background_shift_register_h, 15 - x) << 1) | NTH_BIT(background_shift_register_l, 15 - x);
  268. if (palette) palette |= ((NTH_BIT(attribute_shift_register_h, 7 - x) << 1) | NTH_BIT(attribute_shift_register_l, 7 - x)) << 2;
  269. }
  270. if ((0x10 & mask_flags) && ((0x04 & mask_flags) || pxx >= 8))
  271. {
  272. for (int i = 7; i >= 0; i--)
  273. {
  274. if (oam_0[i].id == 64) continue;
  275. unsigned spx = pxx - oam_0[i].x;
  276. if (spx >= 8) continue;
  277. if (oam_0[i].attributes & 0x40) spx ^= 7;
  278. uint8_t spr_palette = (NTH_BIT(oam_0[i].data_h, 7 - spx) << 1) | NTH_BIT(oam_0[i].data_l, 7 - spx);
  279. if (spr_palette == 0) continue; // Transparent pixel.
  280. if (oam_0[i].id == 0 && palette && pxx != 255) status_flags |= 0x40;
  281. spr_palette |= (oam_0[i].attributes & 3) << 2;
  282. obj_palette = spr_palette + 16;
  283. obj_priority = oam_0[i].attributes & 0x20;
  284. }
  285. }
  286. if (obj_palette && (palette == 0 || obj_priority == 0)) palette = obj_palette;
  287. framebuffer[line * 256 + pxx] = nes_palette[read_memory(0x3F00 + (background_enabled() ? palette : 0))];
  288.  
  289. background_shift_register_l <<= 1;
  290. background_shift_register_h <<= 1;
  291. attribute_shift_register_l = (attribute_shift_register_l << 1) | attribute_latch_l;
  292. attribute_shift_register_h = (attribute_shift_register_h << 1) | attribute_latch_h;
  293. }
  294.  
  295. void dot_340()
  296. {
  297. name_buffer = read_memory(address_buffer);
  298.  
  299. dot = 0xFFFF;
  300. if (line == 239) dot_process = &dot_240_0;
  301. else dot_process = &dot_0_0;
  302. (line = (line + 1) % 262) || (odd_frame = !odd_frame);
  303. }
  304. void dot_339()
  305. {
  306. address_buffer = nametable_address();
  307. dot_process = &dot_340;
  308. }
  309. void dot_338()
  310. {
  311. name_buffer = read_memory(address_buffer);
  312. dot_process = &dot_339;
  313. }
  314. void dot_321()
  315. {
  316. load_sprites();
  317. address_buffer = nametable_address();
  318. dot_process = &dot_nt_even;
  319. }
  320. void dot_0_258()
  321. {
  322. oam_address = 0;
  323. if (dot == 320) dot_process = &dot_321;
  324. }
  325. void dot_261_280()
  326. {
  327. oam_address = 0;
  328. v_update();
  329. if (dot == 304) dot_process = &dot_0_258;
  330. }
  331. void dot_261_258()
  332. {
  333. oam_address = 0;
  334. if (dot == 279) dot_process = &dot_261_280;
  335. }
  336. void dot_261_257()
  337. {
  338. pixel();
  339. reload_shift_registers();
  340. h_update();
  341. dot_process = &dot_261_258;
  342. }
  343. void dot_0_257()
  344. {
  345. evaluate_sprites();
  346. pixel();
  347. reload_shift_registers();
  348. h_update();
  349. dot_process = &dot_0_258;
  350. }
  351. void dot_256()
  352. {
  353. pixel();
  354. background_tile_h = read_memory(address_buffer);
  355. v_scroll();
  356. if (line == 261) dot_process = &dot_261_257;
  357. else dot_process = &dot_0_257;
  358. }
  359. void dot_bgh_even()
  360. {
  361. pixel();
  362. background_tile_h = read_memory(address_buffer);
  363. h_scroll();
  364. dot_process = &dot_nt_odd;
  365. }
  366. void dot_bgh_odd()
  367. {
  368. pixel();
  369. address_buffer += 8;
  370. if(dot == 255) dot_process = &dot_256;
  371. else dot_process = &dot_bgh_even;
  372. }
  373. void dot_bgl_even()
  374. {
  375. pixel();
  376. background_tile_l = read_memory(address_buffer);
  377. dot_process = &dot_bgh_odd;
  378. }
  379. void dot_bgl_odd()
  380. {
  381. pixel();
  382. address_buffer = background_address();
  383. dot_process = &dot_bgl_even;
  384. }
  385. void dot_at_even()
  386. {
  387. pixel();
  388. attribute_buffer = read_memory(address_buffer);
  389. if (v & 0x02) attribute_buffer >>= 2;
  390. if (v & 0x40) attribute_buffer >>= 4;
  391. dot_process = &dot_bgl_odd;
  392. }
  393. void dot_at_odd()
  394. {
  395. pixel();
  396. address_buffer = attributes_address();
  397. dot_process = &dot_at_even;
  398. }
  399. void dot_nt_even()
  400. {
  401. pixel();
  402. name_buffer = read_memory(address_buffer);
  403. dot_process = &dot_at_odd;
  404. }
  405. void dot_nt_odd()
  406. {
  407. pixel();
  408. address_buffer = nametable_address();
  409. reload_shift_registers();
  410. if(dot == 337) dot_process = &dot_338;
  411. else dot_process = &dot_nt_even;
  412. }
  413. void dot_261_1()
  414. {
  415. status_flags &= ~0x60;
  416. address_buffer = nametable_address();
  417. status_flags &= ~0x80;
  418. dot_process = &dot_nt_even;
  419. }
  420. void dot_261_0()
  421. {
  422. dot_process = dot_261_1;
  423. }
  424. void dot_241_2()
  425. {
  426. if (dot == 340)
  427. {
  428. dot = 0xFFFF;
  429. if(line == 261) dot_process = dot_261_0;
  430. (line = (line + 1) % 262) || (odd_frame = !odd_frame);
  431. }
  432. }
  433. void dot_241_1()
  434. {
  435. status_flags |= 0x80;
  436. if (control_flags & 0x80) nmi_flipflop->set();
  437. dot_process = &dot_241_2;
  438. }
  439. void dot_241_0()
  440. {
  441. dot_process = &dot_241_1;
  442. }
  443. void dot_240_1()
  444. {
  445. if(dot == 340)
  446. {
  447. dot = 0xFFFF;
  448. (line = (line + 1) % 262) || (odd_frame = !odd_frame);
  449. dot_process = &dot_241_0;
  450. }
  451. }
  452. void dot_240_0()
  453. {
  454. display->update_frame(framebuffer.data());
  455. dot_process = &dot_240_1;
  456. }
  457. void dot_0_1()
  458. {
  459. clear_oam_1();
  460. address_buffer = nametable_address();
  461. dot_process = &dot_nt_even;
  462. }
  463. void dot_0_0()
  464. {
  465. if (line == 0 && background_enabled() && odd_frame)
  466. {
  467. ++dot;
  468. dot_0_1();
  469. }
  470. else dot_process = &dot_0_1;
  471. }
  472.  
  473. void clock()
  474. {
  475. dot_process();
  476. ++dot;
  477. }
  478. void write(uint8_t index, uint8_t value)
  479. {
  480. data_latch = value;
  481.  
  482. switch (index)
  483. {
  484. case PPUCTRL:
  485. control_flags = value;
  486. t = (~0x0C00 & t) | (0x0C00 & (control_flags << 10));
  487. break;
  488. case PPUMASK:
  489. mask_flags = value;
  490. break;
  491. case OAMADDR:
  492. oam_address = value;
  493. break;
  494. case OAMDATA:
  495. oam_ram[oam_address] = value;
  496. oam_address = (oam_address + 1) % 0x0100;
  497. break;
  498. case PPUSCROLL:
  499. if ((w = !w))
  500. {
  501. x = 0x07 & value;
  502. t = (~0x001F & t) | (0x001F & (value >> 3));
  503. }
  504. else
  505. {
  506. t = (~(0x7000 | 0x03E0) & t) | (0x7000 & (value << 12)) | (0x03E0 & (value << 2));
  507. }
  508. break;
  509. case PPUADDR:
  510. if ((w = !w))
  511. {
  512. t = (~0x3F00 & t) | (0x3F00 & (value << 8));
  513. }
  514. else
  515. {
  516. t = (~0x00FF & t) | (0x00FF & value);
  517. v = t;
  518. }
  519. break;
  520. case PPUDATA:
  521. write_memory(0x3FFF & v, value);
  522. v = (~0x3FFF & v) | (0x3FFF & (v + ((0x04 & control_flags) ? 0x0020 : 0x0001)));
  523. }
  524. }
  525. uint8_t read(uint8_t index)
  526. {
  527. switch (index)
  528. {
  529. case PPUSTATUS:
  530. data_latch = (0x1F & data_latch) | status_flags;
  531. status_flags &= ~0x80;
  532. w = 0;
  533. break;
  534. case OAMDATA:
  535. data_latch = oam_ram[oam_address];
  536. break;
  537. case PPUDATA:
  538. if (~v & 0x3F00)
  539. {
  540. data_latch = data_buffer;
  541. data_buffer = read_memory(0x3FFF & v);
  542. }
  543. else
  544. {
  545. data_latch = read_memory(0x3FFF & v);
  546. data_buffer = read_memory(0x2FFF & v);
  547. }
  548. v = (~0x3FFF & v) | (0x3FFF & (v + ((0x04 & control_flags) ? 0x0020 : 0x0001)));
  549. }
  550. return data_latch;
  551. }
  552. void reset()
  553. {
  554. odd_frame = false;
  555. line = 0;
  556. dot_process = dot_0_0;
  557. dot = 0;
  558. control_flags = 0;
  559. mask_flags = 0;
  560. status_flags = 0;
  561.  
  562. framebuffer.fill(0);
  563. nametable_ram.fill(0xFF);
  564. oam_ram.fill(0);
  565. }
  566. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement