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.

154 lines
4.4KB

  1. #include <limits.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include "nes.h"
  5. #include "render.h"
  6. #include "input.h"
  7. /*
  8. void e6502_print_registers(const e6502_Registers* regs,
  9. FILE* file) {
  10. fprintf(file, "PC: $%04x\n", regs->PC);
  11. fprintf(file, " S: $%02x\n", regs->S);
  12. fprintf(file, " A: $%02x\n", regs->A);
  13. fprintf(file, " X: $%02x\n", regs->X);
  14. fprintf(file, " Y: $%02x\n", regs->Y);
  15. fprintf(file, " P: $%02x\n", regs->P);
  16. }
  17. void e6502_dump_mem(e6502_Core* core, uint16_t addr,
  18. int len, FILE* file) {
  19. for ( ; len > 0; --len, ++addr) {
  20. fprintf(file, "$%04x: %02x\n",
  21. addr, e6502_r8(core, addr));
  22. }
  23. }
  24. void e6502_dump_stack(e6502_Core* core, FILE* file) {
  25. int len = 0x100U + 2U - core->registers.S;
  26. uint16_t addr = e6502_Memory_Stack + 0xFFU;
  27. for ( ; len > 0; --len, --addr) {
  28. fprintf(file, "$%03x: %02x\n",
  29. addr, e6502_r8(core, addr));
  30. }
  31. }
  32. */
  33. #define NS_PER_S (1000U * 1000U * 1000U)
  34. /*
  35. static int t_diff_ns(const struct timespec* b,
  36. const struct timespec* a) {
  37. int sec = (b->tv_sec - a->tv_sec);
  38. int nsec = (b->tv_nsec - a->tv_nsec);
  39. return ((sec * NS_PER_S) + nsec);
  40. }
  41. */
  42. static void t_add_ns(struct timespec* s,
  43. const struct timespec* a,
  44. int b) {
  45. int nsec = a->tv_nsec + b;
  46. s->tv_sec = a->tv_sec + (nsec / NS_PER_S);
  47. s->tv_nsec = nsec % NS_PER_S;
  48. }
  49. extern nes_Renderer sdl_renderer;
  50. extern nes_Input_Reader sdl_input;
  51. static nes sys = {0};
  52. int main(int argc, char* argv[]) {
  53. int status = 0;
  54. int n_loops = (argc > 1) ? atoi(argv[1]) : 0;
  55. if (n_loops <= 0) n_loops = INT_MAX;
  56. status = nes_cart_init_file(&sys.cart, stdin);
  57. nes_Renderer* rend = &sdl_renderer;
  58. if (status == 0) {
  59. status = nes_render_init(rend);
  60. }
  61. nes_Input_Reader* input = &sdl_input;
  62. if (status == 0) {
  63. status = nes_input_init(input);
  64. }
  65. if (status == 0) {
  66. status = nes_init(&sys);
  67. }
  68. if (status == 0) {
  69. nes_reset(&sys);
  70. nes_render(rend, &sys.ppu);
  71. struct timespec t_target = {0};
  72. clock_gettime(CLOCK_MONOTONIC, &t_target);
  73. uint64_t cycle_last_frame = 0;
  74. uint64_t total_cycles = 0;
  75. // int last_frame_rendered = -1;
  76. for (int i = 0; i < n_loops && status == 0; ++i) {
  77. int run = 0;
  78. nes_ppu_Result result = nes_run(&sys, 1U, &run);
  79. total_cycles += run;
  80. /*
  81. float us_run = ( run * 1000. * 1000. *
  82. nes_clock_master_den) /
  83. nes_clock_master_num;
  84. fprintf(stdout, "Ran %f us, %d master cycles (%s)\n",
  85. us_run, run,
  86. status == 0 ? "OK" : "Halted");
  87. */
  88. if ( result == ppu_Result_Ready ||
  89. result == ppu_Result_VBlank_Off) {
  90. status = nes_render(rend, &sys.ppu);
  91. if (status > 0) {
  92. // last_frame_rendered = sys.ppu.frame;
  93. // Sleep to catch up to master clock
  94. uint64_t elapsed_cycles = total_cycles -
  95. cycle_last_frame;
  96. int elapsed_ns = ( elapsed_cycles *
  97. nes_clock_master_den *
  98. NS_PER_S ) /
  99. nes_clock_master_num;
  100. t_add_ns(&t_target, &t_target, elapsed_ns);
  101. clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
  102. &t_target, NULL);
  103. cycle_last_frame = total_cycles;
  104. // Update button states every rendered frame
  105. status = nes_input_update(input, &sys.input);
  106. }
  107. } else if (result == ppu_Result_Halt) {
  108. status = -1;
  109. }
  110. }
  111. float ms_run = ( total_cycles * 1000. *
  112. nes_clock_master_den) /
  113. nes_clock_master_num;
  114. fprintf(stdout, "Ran %f ms, %lu master cycles (%s)\n",
  115. ms_run, total_cycles,
  116. status == 0 ? "OK" : "Halted");
  117. /*
  118. e6502_print_registers(&sys.cpu.registers, stdout);
  119. e6502_dump_stack(&sys.cpu, stdout);
  120. */
  121. }
  122. return status;
  123. }