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.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

342 líneas
12KB

  1. #include <string.h>
  2. #include "ppu.h"
  3. //#define DEBUG "PPU"
  4. #include "log.h"
  5. static const uint8_t mirror_schemes[][4] = {
  6. [nes_Mirror_Horizontal] = {0, 0, 1, 1},
  7. [nes_Mirror_Vertical] = {0, 1, 0, 1},
  8. [nes_Mirror_Only_0] = {0, 0, 0, 0},
  9. [nes_Mirror_Only_1] = {1, 1, 1, 1},
  10. [nes_Mirror_Four] = {0, 1, 2, 3},
  11. };
  12. void nes_ppu_set_mirroring(nes_PPU_Memory* mem,
  13. nes_Nametable_Mirroring mirror) {
  14. for (int i = 0; i < 4; ++i) {
  15. mem->bank[nes_PPU_Nametable_Bank_Index + i] =
  16. vram_page(mem, (int)mirror_schemes[mirror][i]);
  17. }
  18. }
  19. void nes_ppu_find_hit_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
  20. int sprite_bank = !!(mem->ctrl & ppu_Control_Sprite_Bank);
  21. oam_sprite sprite = *(oam_sprite*)mem->oam;
  22. int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
  23. 16 : 8;
  24. int stride = 1;
  25. int y_off = 0;
  26. if (sprite.attr & oam_Attr_Flip_Y) {
  27. stride = -1;
  28. y_off = sprite_height - 1;
  29. sprite_bank = (sprite.index & 1);
  30. sprite.index &= 0xFEU;
  31. }
  32. sprite_bank = (sprite_bank << 2) | (sprite.index >> 6);
  33. const int data_off = ((sprite.index & 0x3FU) << 4) +
  34. ((y_off & 8) << 1) + (y_off & 7);
  35. uint8_t* data = mem->bank[sprite_bank] + data_off;
  36. ppu->hit_line = -1;
  37. if (sprite.y + 1 <= nes_ppu_render_h && sprite.y > 0) {
  38. for (int line = 0; line < sprite_height; ++line) {
  39. if (data[0] | data[8]) {
  40. ppu->hit_line = sprite.y + 1 + line + nes_ppu_visible_line;
  41. break;
  42. }
  43. data += stride;
  44. }
  45. }
  46. }
  47. void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
  48. uint8_t* scanline_ptr = ppu->screen_data +
  49. ( nes_ppu_render_w *
  50. (ppu->scanline - nes_ppu_visible_line));
  51. uint8_t* ptr = scanline_ptr;
  52. if (!(mem->mask & ppu_Mask_Back)) {
  53. memset(ptr, 0xFFU, nes_ppu_render_w);
  54. /*
  55. // Emulate the happy part of the backdrop override quirk
  56. int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ?
  57. (mem->addr & 0x1FU) : 0;
  58. // Don't use the rendering palette (masked transparency)
  59. memset(ptr, mem->pal_bank[0x300 + pal_idx],
  60. nes_ppu_render_w);
  61. */
  62. } else {
  63. int back_bank = nes_PPU_Nametable_Bank_Index +
  64. ((mem->addr >> 10) & 3);
  65. int y_coarse = (mem->addr >> 5) & 0x1F;
  66. int y_fine = mem->addr >> 12;
  67. int x_coarse = mem->addr & 0x1FU;
  68. const int bank_off = !!(mem->ctrl & ppu_Control_Back_Bank) << 2;
  69. const uint8_t* nametable = mem->bank[back_bank] + (y_coarse * 32) + x_coarse;
  70. const uint8_t* attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);
  71. // Partial left column
  72. ptr += 8 - mem->x;
  73. // Omit if left column is masked
  74. if (mem->mask & ppu_Mask_Left_Back) {
  75. const uint8_t* pal = mem->palette +
  76. (((attrs[x_coarse >> 2] >>
  77. ( (x_coarse & 2) +
  78. ((y_coarse & 2) << 1))) & 3) << 2);
  79. const int ch = *nametable;
  80. const int bank = (ch >> 6) + bank_off;
  81. const int addr_off = ((ch & 0x3F) << 4) + y_fine;
  82. const uint8_t* data = mem->bank[bank] + addr_off;
  83. const uint8_t pl0 = data[0];
  84. const uint8_t pl1 = data[8];
  85. const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  86. const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  87. switch (mem->x) {
  88. case 0: ptr[-8] = pal[(pat1 >> 6) & 3];
  89. case 1: ptr[-7] = pal[(pat0 >> 6) & 3];
  90. case 2: ptr[-6] = pal[(pat1 >> 4) & 3];
  91. case 3: ptr[-5] = pal[(pat0 >> 4) & 3];
  92. case 4: ptr[-4] = pal[(pat1 >> 2) & 3];
  93. case 5: ptr[-3] = pal[(pat0 >> 2) & 3];
  94. case 6: ptr[-2] = pal[(pat1 >> 0) & 3];
  95. case 7: ptr[-1] = pal[(pat0 >> 0) & 3];
  96. }
  97. }
  98. // TODO: Mapper ppu_bus
  99. ++x_coarse;
  100. ++nametable;
  101. void __attribute__((always_inline)) inline render_bg(void) {
  102. const uint8_t* pal = mem->palette +
  103. (((attrs[x_coarse >> 2] >>
  104. ( (x_coarse & 2) +
  105. ((y_coarse & 2) << 1))) & 3) << 2);
  106. const int ch = *nametable;
  107. const int bank = (ch >> 6) + bank_off;
  108. const int addr_off = ((ch & 0x3F) << 4) + y_fine;
  109. const uint8_t* data = mem->bank[bank] + addr_off;
  110. const uint8_t pl0 = data[0];
  111. const uint8_t pl1 = data[8];
  112. const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  113. const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  114. ptr[0] = pal[(pat1 >> 6) & 3];
  115. ptr[1] = pal[(pat0 >> 6) & 3];
  116. ptr[2] = pal[(pat1 >> 4) & 3];
  117. ptr[3] = pal[(pat0 >> 4) & 3];
  118. ptr[4] = pal[(pat1 >> 2) & 3];
  119. ptr[5] = pal[(pat0 >> 2) & 3];
  120. ptr[6] = pal[(pat1 >> 0) & 3];
  121. ptr[7] = pal[(pat0 >> 0) & 3];
  122. ptr += 8;
  123. }
  124. // Left Nametable
  125. for (; x_coarse < 32; ++x_coarse) {
  126. render_bg();
  127. // TODO: Mapper ppu_bus
  128. ++nametable;
  129. }
  130. // Right Nametable
  131. back_bank ^= 0b01;
  132. nametable = mem->bank[back_bank] + (y_coarse * 32);
  133. attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3);
  134. for (x_coarse = 0; x_coarse < (mem->addr & 0x1FU); ++x_coarse) {
  135. render_bg();
  136. // TODO: Mapper ppu_bus
  137. ++nametable;
  138. }
  139. // Partial right column
  140. {
  141. const uint8_t* pal = mem->palette +
  142. (((attrs[x_coarse >> 2] >>
  143. ( (x_coarse & 2) +
  144. ((y_coarse & 2) << 1))) & 3) << 2);
  145. const int ch = *nametable;
  146. const int bank = (ch >> 6) + bank_off;
  147. const int addr_off = ((ch & 0x3F) << 4) + y_fine;
  148. const uint8_t* data = mem->bank[bank] + addr_off;
  149. const uint8_t pl0 = data[0];
  150. const uint8_t pl1 = data[8];
  151. const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  152. const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  153. switch (mem->x) {
  154. case 8: ptr[7] = pal[(pat0 >> 0) & 3];
  155. case 7: ptr[6] = pal[(pat1 >> 0) & 3];
  156. case 6: ptr[5] = pal[(pat0 >> 2) & 3];
  157. case 5: ptr[4] = pal[(pat1 >> 2) & 3];
  158. case 4: ptr[3] = pal[(pat0 >> 4) & 3];
  159. case 3: ptr[2] = pal[(pat1 >> 4) & 3];
  160. case 2: ptr[1] = pal[(pat0 >> 6) & 3];
  161. case 1: ptr[0] = pal[(pat1 >> 6) & 3];
  162. }
  163. }
  164. }
  165. // TODO: Mapper ppu_bus
  166. if (!(mem->mask & ppu_Mask_Left_Back)) {
  167. memset(scanline_ptr, 0xFFU, 8);
  168. }
  169. // TODO: Mapper VROM switch
  170. /*
  171. if (mem->mask & ppu_Mask_Sprite) {
  172. // This time, we do need overflow protection
  173. // uint8_t sprite_data[nes_ppu_render_w + 8] = {0};
  174. const int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
  175. 16 : 8;
  176. const int scanline = ppu->scanline - nes_ppu_visible_line;
  177. int bank = !!(mem->ctrl & ppu_Control_Sprite_Bank) << 2;
  178. int n_sprites = 0;
  179. const oam_sprite* sprites = (oam_sprite*)mem->oam;
  180. const oam_sprite* sprite = sprites +
  181. NES_PPU_SPRITE_COUNT - 1;
  182. for ( ; sprite >= sprites && n_sprites < 8; --sprite) {
  183. int y = sprite->y + 1;
  184. if (y > scanline || y + sprite_height <= scanline) {
  185. continue;
  186. }
  187. int y_off = scanline - y;
  188. if (sprite->attr & oam_Attr_Flip_Y) y_off = sprite_height - y_off - 1;
  189. int ch = sprite->index;
  190. if (mem->ctrl & ppu_Control_Sprite_Size) {
  191. bank = (ch & 1) << 2;
  192. ch &= 0xFEU;
  193. }
  194. bank += (ch >> 6);
  195. const int addr_off = ((ch & 0x3fU) << 4) + ((y_off & 8) << 1) + (y_off & 7);
  196. const uint8_t* data = mem->bank[bank] + addr_off;
  197. const uint8_t pl0 = data[0];
  198. const uint8_t pl1 = data[8];
  199. const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
  200. const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);
  201. const uint8_t* pal = &mem->palette[0x10 + ((sprite->attr & oam_Attr_Pal_Mask) << 2)];
  202. uint8_t* dst = scanline_ptr + sprite->x;
  203. const int over_x = ((int)sprite->x + 8) - nes_ppu_render_w;
  204. // TODO: X Flip
  205. switch (over_x) {
  206. default:
  207. { int pal_idx = (pat1 << 0) & 3;
  208. if ( ( !(sprite->attr & oam_Attr_Background) ||
  209. dst[7] == 0xFFU)
  210. && pal_idx) {
  211. dst[7] = pal[pal_idx];
  212. }
  213. }
  214. case 1:
  215. { int pal_idx = (pat0 << 0) & 3;
  216. if ( ( !(sprite->attr & oam_Attr_Background) ||
  217. dst[6] == 0xFFU)
  218. && pal_idx) {
  219. dst[6] = pal[pal_idx];
  220. }
  221. }
  222. case 2:
  223. { int pal_idx = (pat1 << 2) & 3;
  224. if ( ( !(sprite->attr & oam_Attr_Background) ||
  225. dst[5] == 0xFFU)
  226. && pal_idx) {
  227. dst[5] = pal[pal_idx];
  228. }
  229. }
  230. case 3:
  231. { int pal_idx = (pat0 << 2) & 3;
  232. if ( ( !(sprite->attr & oam_Attr_Background) ||
  233. dst[4] == 0xFFU)
  234. && pal_idx) {
  235. dst[4] = pal[pal_idx];
  236. }
  237. }
  238. case 4:
  239. { int pal_idx = (pat1 << 4) & 3;
  240. if ( ( !(sprite->attr & oam_Attr_Background) ||
  241. dst[3] == 0xFFU)
  242. && pal_idx) {
  243. dst[3] = pal[pal_idx];
  244. }
  245. }
  246. case 5:
  247. { int pal_idx = (pat0 << 4) & 3;
  248. if ( ( !(sprite->attr & oam_Attr_Background) ||
  249. dst[2] == 0xFFU)
  250. && pal_idx) {
  251. dst[2] = pal[pal_idx];
  252. }
  253. }
  254. case 6:
  255. { int pal_idx = (pat1 << 6) & 3;
  256. if ( ( !(sprite->attr & oam_Attr_Background) ||
  257. dst[1] == 0xFFU)
  258. && pal_idx) {
  259. dst[1] = pal[pal_idx];
  260. }
  261. }
  262. case 7:
  263. { int pal_idx = (pat0 << 6) & 3;
  264. if ( ( !(sprite->attr & oam_Attr_Background) ||
  265. dst[0] == 0xFFU)
  266. && pal_idx) {
  267. dst[0] = pal[pal_idx];
  268. }
  269. }
  270. }
  271. ++n_sprites;
  272. }
  273. if (n_sprites >= 8) {
  274. mem->status |= ppu_Status_Overflow;
  275. } else {
  276. mem->status &= ~ppu_Status_Overflow;
  277. }
  278. }
  279. */
  280. // Increment internal registers
  281. if (mem->mask & (ppu_Mask_Sprite | ppu_Mask_Back)) {
  282. uint16_t mask = 0b10000011111;
  283. mem->addr = (mem->addr & ~mask) | (mem->t & mask);
  284. int y_scroll = (mem->addr >> 12) |
  285. ((mem->addr >> 2) & 0xF8U);
  286. if (nes_ppu_render_h - 1 == y_scroll) {
  287. y_scroll = 0;
  288. mem->addr ^= 0x800;
  289. } else if (0xFFU == y_scroll) {
  290. y_scroll = 0;
  291. } else {
  292. ++y_scroll;
  293. }
  294. mem->addr = (mem->addr & ~0b111001111100000) |
  295. ((y_scroll & 7) << 12) |
  296. ((y_scroll & 0xF8U) << 2);
  297. }
  298. }