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 kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

169 lines
4.8KB

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