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.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

426 Zeilen
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. };