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.

161 líneas
4.4KB

  1. #include <stddef.h>
  2. #include "nes.h"
  3. #include "port.h"
  4. //#define NESE_DEBUG "NES"
  5. #include "log.h"
  6. void nes_init(nes* sys) {
  7. f6502_init(&sys->core);
  8. // TODO: Init PPU
  9. // TODO: Init APU
  10. }
  11. void nes_reset(nes* sys) {
  12. f6502_reset(&sys->core);
  13. // TODO: Reset PPU
  14. // TODO: Reset APU
  15. }
  16. void nes_done(nes* sys) {
  17. // TODO: deallocate RAM, etc.
  18. }
  19. static int nes_vsync(nes* sys, void* plat) {
  20. int status = 0;
  21. sys->core.memory.ppu.status |= ppu_Status_VBlank;
  22. if (sys->core.memory.ppu.ctrl & ppu_Control_VBlank) {
  23. LOGD("VBlank NMI");
  24. f6502_set_NMI(&sys->core, 1);
  25. }
  26. // TODO: APU Sync
  27. // TODO: APU Frame IRQ
  28. nes_Memory* mem = &sys->core.memory;
  29. if (0 == status && NULL != mem->mapper.vsync) {
  30. mem->mapper.vsync(&mem->mapper);
  31. status = nese_update_input(plat, &mem->input);
  32. }
  33. return status;
  34. }
  35. static int nes_hsync(nes* sys, void* plat) {
  36. int status = 0;
  37. // TODO: APU sync
  38. if (sys->ppu.scanline < nes_ppu_postrender_line) {
  39. if (sys->ppu.scanline < nes_ppu_visible_line) {
  40. if ( sys->core.memory.ppu.mask &
  41. (ppu_Mask_Sprite | ppu_Mask_Back)) {
  42. sys->core.memory.ppu.addr =
  43. sys->core.memory.ppu.t;
  44. }
  45. } else {
  46. nes_ppu_render_line(&sys->ppu,
  47. &sys->core.memory.ppu);
  48. nese_line_ready(
  49. plat,
  50. sys->ppu.screen_data +
  51. ( nes_ppu_render_w *
  52. ( sys->ppu.scanline -
  53. nes_ppu_visible_line)),
  54. sys->ppu.scanline - nes_ppu_visible_line
  55. );
  56. }
  57. }
  58. sys->ppu.scanline++;
  59. if (nes_ppu_frame_end_line == sys->ppu.scanline) {
  60. sys->ppu.scanline = nes_ppu_prerender_line;
  61. }
  62. switch (sys->ppu.scanline) {
  63. case nes_ppu_prerender_line:
  64. { nes_PPU_Memory* mem = &sys->core.memory.ppu;
  65. f6502_set_NMI(&sys->core, 0);
  66. mem->status &= ~(ppu_Status_VBlank | ppu_Status_Hit);
  67. nes_ppu_find_hit_line(&sys->ppu, mem);
  68. // Emulate the happy part of the backdrop override quirk
  69. int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ?
  70. (mem->addr & 0x1FU) : 0;
  71. LOGD("Background: %d", pal_idx);
  72. // Don't use the rendering palette (masked transparency)
  73. status = nese_frame_start(plat, mem->pal_bank[0x300 + pal_idx] & 0x3FU);
  74. } break;
  75. case nes_ppu_postrender_line:
  76. status = nese_frame_ready(plat);
  77. break;
  78. case nes_ppu_vblank_line:
  79. status = nes_vsync(sys, plat);
  80. break;
  81. }
  82. return status;
  83. }
  84. #define mapper_hsync_dot (300U)
  85. int nes_loop(nes* sys, void* plat) {
  86. int status = 0;
  87. int dot = 0;
  88. bool mapper_hsync = false;
  89. while (0 == status) {
  90. // TODO: Move to inline PPU function?
  91. int target_dot = nes_ppu_scanline_dots;
  92. if ( sys->core.memory.mapper.hsync &&
  93. sys->ppu.scanline > nes_ppu_prerender_line &&
  94. sys->ppu.scanline < nes_ppu_postrender_line &&
  95. dot < mapper_hsync_dot) {
  96. target_dot = mapper_hsync_dot;
  97. mapper_hsync = true;
  98. }
  99. if ( sys->ppu.hit_line == sys->ppu.scanline &&
  100. ( sys->core.memory.ppu.mask &
  101. (ppu_Mask_Sprite | ppu_Mask_Back)) &&
  102. !(sys->core.memory.ppu.status & ppu_Status_Hit)) {
  103. int hit_dot = ((oam_sprite*)sys->core.memory.ppu.oam)->x;
  104. if (dot >= hit_dot) {
  105. LOGD("Line %d sprite 0 hit @ %d", sys->ppu.hit_line, hit_dot);
  106. sys->core.memory.ppu.status |= ppu_Status_Hit;
  107. } else {
  108. target_dot = hit_dot;
  109. }
  110. }
  111. if (target_dot > dot) {
  112. int cpu_cycles = (target_dot - dot + 2) / 3;
  113. dot += 3 * f6502_step(&sys->core, cpu_cycles);
  114. }
  115. if (mapper_hsync && dot >= mapper_hsync_dot) {
  116. nes_Memory* mem = &sys->core.memory;
  117. mem->mapper.hsync(&mem->mapper);
  118. mapper_hsync = false;
  119. }
  120. // It's possible we returned due to a pending interrupt.
  121. if (dot >= nes_ppu_scanline_dots) {
  122. dot -= nes_ppu_scanline_dots;
  123. status = nes_hsync(sys, plat);
  124. }
  125. }
  126. return status;
  127. }