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.

426 lines
14KB

  1. #include <string.h>
  2. #include "ppu.h"
  3. //#define NESE_DEBUG "PPU"
  4. #include "log.h"
  5. #include "serdes.h"
  6. void nes_ppu_init(nes_PPU* ppu, nes_PPU_Memory* mem) {
  7. uint8_t* pal = mem->palette;
  8. pal[0] = pal[4] = pal[8] = pal[12] =
  9. pal[16] = pal[20] = pal[24] =
  10. pal[28] = 0xFFU;
  11. ppu->hit_line = -1;
  12. }
  13. static const uint8_t mirror_schemes[][4] = {
  14. [nes_Mirror_Horizontal] = {0, 0, 1, 1},
  15. [nes_Mirror_Vertical] = {0, 1, 0, 1},
  16. [nes_Mirror_Only_0] = {0, 0, 0, 0},
  17. [nes_Mirror_Only_1] = {1, 1, 1, 1},
  18. [nes_Mirror_Four] = {0, 1, 2, 3},
  19. };
  20. void nes_ppu_set_mirroring(nes_PPU_Memory* mem,
  21. nes_Nametable_Mirroring mirror) {
  22. for (int i = 0; i < 4; ++i) {
  23. mem->bank[nes_PPU_Nametable_Bank_Index + i] =
  24. vram_page(mem, (int)mirror_schemes[mirror][i]);
  25. }
  26. }
  27. void nes_ppu_find_hit_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
  28. int sprite_bank = !!(mem->ctrl & ppu_Control_Sprite_Bank);
  29. oam_sprite sprite = *(oam_sprite*)mem->oam;
  30. int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
  31. 16 : 8;
  32. int stride = 1;
  33. int y_off = 0;
  34. if (sprite.attr & oam_Attr_Flip_Y) {
  35. stride = -1;
  36. y_off = sprite_height - 1;
  37. sprite_bank = (sprite.index & 1);
  38. sprite.index &= 0xFEU;
  39. }
  40. sprite_bank = (sprite_bank << 2) | (sprite.index >> 6);
  41. const int data_off = ((sprite.index & 0x3FU) << 4) +
  42. ((y_off & 8) << 1) + (y_off & 7);
  43. uint8_t* data = mem->bank[sprite_bank] + data_off;
  44. ppu->hit_line = -1;
  45. if (sprite.y + 1 <= nes_ppu_render_h && sprite.y > 0) {
  46. for (int line = 0; line < sprite_height; ++line) {
  47. if (data[0] | data[8]) {
  48. ppu->hit_line = sprite.y + 1 + line + nes_ppu_visible_line;
  49. break;
  50. }
  51. data += stride;
  52. }
  53. }
  54. }
  55. static const uint8_t sprite_rev[256] = {
  56. 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
  57. 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
  58. 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
  59. 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
  60. 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
  61. 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
  62. 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
  63. 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
  64. 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
  65. 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
  66. 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
  67. 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
  68. 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
  69. 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
  70. 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
  71. 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
  72. 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
  73. 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
  74. 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
  75. 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
  76. 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
  77. 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
  78. 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
  79. 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
  80. 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
  81. 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
  82. 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
  83. 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
  84. 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
  85. 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
  86. 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
  87. 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF,
  88. };
  89. void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
  90. uint8_t* scanline_ptr = ppu->screen_data +
  91. ( nes_ppu_render_w *
  92. (ppu->scanline - nes_ppu_visible_line));
  93. uint8_t* ptr = scanline_ptr;
  94. if (!(mem->mask & ppu_Mask_Back)) {
  95. memset(ptr, 0xFFU, nes_ppu_render_w);
  96. } else {
  97. int back_bank = nes_PPU_Nametable_Bank_Index +
  98. ((mem->addr >> 10) & 3);
  99. int y_coarse = (mem->addr >> 5) & 0x1F;
  100. int y_fine = mem->addr >> 12;
  101. int x_coarse = mem->addr & 0x1FU;
  102. const int bank_off = !!(mem->ctrl & ppu_Control_Back_Bank) << 2;
  103. const uint8_t* nametable = mem->bank[back_bank] + (y_coarse * 32) + x_coarse;
  104. const uint8_t* attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);
  105. // Partial left column
  106. ptr += 8 - mem->x;
  107. // Omit if left column is masked
  108. if (mem->mask & ppu_Mask_Left_Back) {
  109. const uint8_t* pal = mem->palette +
  110. (((attrs[x_coarse >> 2] >>
  111. ( (x_coarse & 2) +
  112. ((y_coarse & 2) << 1))) & 3) << 2);
  113. const int ch = *nametable;
  114. const int bank = (ch >> 6) + bank_off;
  115. const int addr_off = ((ch & 0x3F) << 4) + y_fine;
  116. const uint8_t* data = mem->bank[bank] + addr_off;
  117. const uint8_t pl0 = data[0];
  118. const uint8_t pl1 = data[8];
  119. const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  120. const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  121. switch (mem->x) {
  122. case 0: ptr[-8] = pal[(pat1 >> 6) & 3];
  123. case 1: ptr[-7] = pal[(pat0 >> 6) & 3];
  124. case 2: ptr[-6] = pal[(pat1 >> 4) & 3];
  125. case 3: ptr[-5] = pal[(pat0 >> 4) & 3];
  126. case 4: ptr[-4] = pal[(pat1 >> 2) & 3];
  127. case 5: ptr[-3] = pal[(pat0 >> 2) & 3];
  128. case 6: ptr[-2] = pal[(pat1 >> 0) & 3];
  129. case 7: ptr[-1] = pal[(pat0 >> 0) & 3];
  130. }
  131. }
  132. // TODO: Mapper ppu_bus
  133. ++x_coarse;
  134. ++nametable;
  135. void __attribute__((always_inline)) inline render_bg(void) {
  136. const uint8_t* pal = mem->palette +
  137. (((attrs[x_coarse >> 2] >>
  138. ( (x_coarse & 2) +
  139. ((y_coarse & 2) << 1))) & 3) << 2);
  140. const int ch = *nametable;
  141. const int bank = (ch >> 6) + bank_off;
  142. const int addr_off = ((ch & 0x3F) << 4) + y_fine;
  143. const uint8_t* data = mem->bank[bank] + addr_off;
  144. const uint8_t pl0 = data[0];
  145. const uint8_t pl1 = data[8];
  146. const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  147. const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  148. ptr[0] = pal[(pat1 >> 6) & 3];
  149. ptr[1] = pal[(pat0 >> 6) & 3];
  150. ptr[2] = pal[(pat1 >> 4) & 3];
  151. ptr[3] = pal[(pat0 >> 4) & 3];
  152. ptr[4] = pal[(pat1 >> 2) & 3];
  153. ptr[5] = pal[(pat0 >> 2) & 3];
  154. ptr[6] = pal[(pat1 >> 0) & 3];
  155. ptr[7] = pal[(pat0 >> 0) & 3];
  156. ptr += 8;
  157. }
  158. // Left Nametable
  159. for (; x_coarse < 32; ++x_coarse) {
  160. render_bg();
  161. // TODO: Mapper ppu_bus
  162. ++nametable;
  163. }
  164. // Right Nametable
  165. back_bank ^= 0b01;
  166. nametable = mem->bank[back_bank] + (y_coarse * 32);
  167. attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);
  168. for (x_coarse = 0; x_coarse < (mem->addr & 0x1FU); ++x_coarse) {
  169. render_bg();
  170. // TODO: Mapper ppu_bus
  171. ++nametable;
  172. }
  173. // Partial right column
  174. {
  175. const uint8_t* pal = mem->palette +
  176. (((attrs[x_coarse >> 2] >>
  177. ( (x_coarse & 2) +
  178. ((y_coarse & 2) << 1))) & 3) << 2);
  179. const int ch = *nametable;
  180. const int bank = (ch >> 6) + bank_off;
  181. const int addr_off = ((ch & 0x3F) << 4) + y_fine;
  182. const uint8_t* data = mem->bank[bank] + addr_off;
  183. const uint8_t pl0 = data[0];
  184. const uint8_t pl1 = data[8];
  185. const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  186. const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  187. switch (mem->x) {
  188. case 8: ptr[7] = pal[(pat0 >> 0) & 3];
  189. case 7: ptr[6] = pal[(pat1 >> 0) & 3];
  190. case 6: ptr[5] = pal[(pat0 >> 2) & 3];
  191. case 5: ptr[4] = pal[(pat1 >> 2) & 3];
  192. case 4: ptr[3] = pal[(pat0 >> 4) & 3];
  193. case 3: ptr[2] = pal[(pat1 >> 4) & 3];
  194. case 2: ptr[1] = pal[(pat0 >> 6) & 3];
  195. case 1: ptr[0] = pal[(pat1 >> 6) & 3];
  196. }
  197. }
  198. }
  199. // TODO: Mapper ppu_bus
  200. if (!(mem->mask & ppu_Mask_Left_Back)) {
  201. memset(scanline_ptr, 0xFFU, 8);
  202. }
  203. // TODO: Mapper VROM switch
  204. if (mem->mask & ppu_Mask_Sprite) {
  205. const int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
  206. 16 : 8;
  207. const int scanline = ppu->scanline - nes_ppu_visible_line;
  208. int sprite_bank = !!(mem->ctrl & ppu_Control_Sprite_Bank) << 2;
  209. uint8_t foreground[nes_ppu_render_w];
  210. memset(foreground, 0xFFU, nes_ppu_render_w);
  211. int n_sprites = 0;
  212. const oam_sprite* sprites = (oam_sprite*)mem->oam;
  213. const oam_sprite* sprite = sprites +
  214. NES_PPU_SPRITE_COUNT - 1;
  215. for ( ; sprite >= sprites && n_sprites < 8; --sprite) {
  216. int y = sprite->y + 1;
  217. if (y > scanline || y + sprite_height <= scanline) {
  218. continue;
  219. }
  220. int y_off = scanline - y;
  221. if (sprite->attr & oam_Attr_Flip_Y) {
  222. y_off = sprite_height - y_off - 1;
  223. }
  224. int bank = sprite_bank;
  225. int ch = sprite->index;
  226. if (mem->ctrl & ppu_Control_Sprite_Size) {
  227. bank = (ch & 1) << 2;
  228. ch &= 0xFEU;
  229. }
  230. bank += (ch >> 6);
  231. const int addr_off = ((ch & 0x3fU) << 4) +
  232. ((y_off & 8) << 1) +
  233. (y_off & 7);
  234. const uint8_t* data = mem->bank[bank] + addr_off;
  235. const uint8_t pl0 = data[0];
  236. const uint8_t pl1 = data[8];
  237. int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  238. int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  239. const uint8_t* pal = &mem->palette[0x10 + ((sprite->attr & oam_Attr_Pal_Mask) << 2)];
  240. const uint8_t* back =
  241. (sprite->attr & oam_Attr_Background) ?
  242. (scanline_ptr + sprite->x) : NULL;
  243. uint8_t* dst = foreground + sprite->x;
  244. const int over_x = ((int)sprite->x + 8) - nes_ppu_render_w;
  245. if (sprite->attr & oam_Attr_Flip_X) {
  246. uint8_t tmp = pat0;
  247. pat0 = sprite_rev[pat1];
  248. pat1 = sprite_rev[tmp];
  249. }
  250. #define do_sprite(idx, pi, shift) \
  251. if (back && back[idx] != 0xFFU) { \
  252. dst[idx] = back[idx]; \
  253. } else { \
  254. int pal_idx = (pat##pi >> shift) & 3; \
  255. if (pal_idx) dst[idx] = pal[pal_idx]; \
  256. }
  257. switch (over_x) {
  258. default:
  259. do_sprite(7, 0, 0);
  260. case 1:
  261. do_sprite(6, 1, 0);
  262. case 2:
  263. do_sprite(5, 0, 2);
  264. case 3:
  265. do_sprite(4, 1, 2);
  266. case 4:
  267. do_sprite(3, 0, 4);
  268. case 5:
  269. do_sprite(2, 1, 4);
  270. case 6:
  271. do_sprite(1, 0, 6);
  272. case 7:
  273. do_sprite(0, 1, 6);
  274. }
  275. ++n_sprites;
  276. }
  277. if (n_sprites >= 8) {
  278. mem->status |= ppu_Status_Overflow;
  279. } else {
  280. mem->status &= ~ppu_Status_Overflow;
  281. }
  282. // Mask column 0?
  283. if (!(mem->mask & ppu_Mask_Left_Sprite)) {
  284. memset(foreground, 0xFFU, 8);
  285. }
  286. // Overlay the sprites
  287. const uint8_t* fg = foreground;
  288. uint8_t* dst = scanline_ptr;
  289. for (int i = 0; i < nes_ppu_render_w; ++i) {
  290. if (*fg != 0xFFU) *dst = *fg;
  291. ++fg;
  292. ++dst;
  293. }
  294. }
  295. // Increment internal registers
  296. if (mem->mask & (ppu_Mask_Sprite | ppu_Mask_Back)) {
  297. uint16_t mask = 0b10000011111;
  298. mem->addr = (mem->addr & ~mask) | (mem->t & mask);
  299. int y_scroll = (mem->addr >> 12) |
  300. ((mem->addr >> 2) & 0xF8U);
  301. if (nes_ppu_render_h - 1 == y_scroll) {
  302. y_scroll = 0;
  303. mem->addr ^= 0x800;
  304. } else if (0xFFU == y_scroll) {
  305. y_scroll = 0;
  306. } else {
  307. ++y_scroll;
  308. }
  309. mem->addr = (mem->addr & ~0b111001111100000) |
  310. ((y_scroll & 7) << 12) |
  311. ((y_scroll & 0xF8U) << 2);
  312. }
  313. }
  314. /*
  315. static int nes_ppu_chr_ram_size(const void* _ppu, const void*) {
  316. const nes_PPU_Memory* ppu = (const nes_PPU_Memory*)_ppu;
  317. return (ppu->chr_ram ?
  318. ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE :
  319. 0);
  320. }
  321. static int nes_ppu_read_chr_ram(void* dst, const void* src,
  322. int avail, const void*) {
  323. int size = 0;
  324. nes_PPU_Memory* ppu = (nes_PPU_Memory*)dst;
  325. if (ppu->chr_ram) {
  326. size = ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
  327. if (size > avail) size = avail;
  328. memcpy(ppu->chr_ram, src, size);
  329. }
  330. return size;
  331. }
  332. static int nes_ppu_write_chr_ram(void* dst, const void* src,
  333. int avail, const void*) {
  334. int size = 0;
  335. const nes_PPU_Memory* ppu = (const nes_PPU_Memory*)src;
  336. if (ppu->chr_ram) {
  337. size = ppu->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
  338. if (size > avail) size = avail;
  339. memcpy(dst, ppu->chr_ram, size);
  340. }
  341. return size;
  342. }
  343. static const Serdes_IO ppu_serdes_io = {
  344. .read = nes_ppu_read_chr_ram,
  345. .write = nes_ppu_write_chr_ram,
  346. .in_size = nes_ppu_chr_ram_size,
  347. .out_size = nes_ppu_chr_ram_size,
  348. };
  349. */
  350. static void* ppu_chr_ram_ptr(const void* _ppu) {
  351. return ((nes_PPU_Memory*)_ppu)->chr_ram;
  352. }
  353. static size_t ppu_chr_ram_size(const void* _ppu) {
  354. return ((nes_PPU_Memory*)_ppu)->n_chr_banks * NES_CHR_ROM_PAGE_SIZE;
  355. }
  356. static Serdes_Ptr_Ref ppu_chr_ram_ref = {
  357. .ptr = ppu_chr_ram_ptr,
  358. .size = ppu_chr_ram_size,
  359. };
  360. const Serdes_Item nes_ppu_memory_serdes[] = {
  361. {"CRAM", 0, &serdes_mem_ptr, &ppu_chr_ram_ref},
  362. {"VRAM", offsetof(nes_PPU_Memory, ctrl), &serdes_mem, (void*)(sizeof(nes_PPU_Memory) - offsetof(nes_PPU_Memory, ctrl))},
  363. {0}
  364. };