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.
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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. }