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个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

114 行
3.2KB

  1. #include <stdio.h>
  2. #include "nes.h"
  3. #include "mapper.h"
  4. uint8_t nes_mem_read(nes* sys, uint16_t addr) {
  5. uint8_t val = 0;
  6. if (addr < nes_mem_ppu_start) {
  7. addr = (addr - nes_mem_ram_start) & (nes_mem_ram_size - 1);
  8. val = sys->ram[addr];
  9. } else if (addr < nes_mem_apu_start) {
  10. addr = (addr - nes_mem_ppu_start) & (nes_ppu_map_size - 1);
  11. val = nes_ppu_read(&sys->ppu, nes_mem_ppu_start + addr);
  12. } else if ( nes_input_1_reg == addr ||
  13. nes_input_2_reg == addr) {
  14. val = nes_input_read(&sys->input, addr);
  15. } else if (addr < nes_mem_exp_start) {
  16. val = nes_apu_read(&sys->apu, addr);
  17. } else {
  18. val = nes_map_read(sys->cart.mapper, addr);
  19. }
  20. return val;
  21. }
  22. void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) {
  23. if (addr < nes_mem_ppu_start) {
  24. addr = (addr - nes_mem_ram_start) & (nes_mem_ram_size - 1);
  25. sys->ram[addr] = val;
  26. } else if (addr < nes_mem_apu_start) {
  27. addr = (addr - nes_mem_ppu_start) & (nes_ppu_map_size - 1);
  28. nes_ppu_write(&sys->ppu, nes_mem_ppu_start + addr, val);
  29. } else if (nes_ppu_dma_reg == addr) {
  30. OAM_LOG("PPU: OAM DMA $%02x00 > $%02x\n", val, sys->ppu.oam_addr);
  31. uint8_t* buf = (uint8_t*)sys->ppu.oam;
  32. for (int i = 0; i < nes_ppu_oam_size; ++i) {
  33. buf[(uint8_t)(i + sys->ppu.oam_addr)] =
  34. nes_mem_read(sys, ((uint16_t)val << 8) + i);
  35. }
  36. sys->cpu.cycle += 513U;
  37. // Other subsystem cycles are driven by CPU cycles
  38. } else if (addr == nes_input_set_reg) {
  39. nes_input_write(&sys->input, addr, val);
  40. } else if (addr < nes_mem_exp_start) {
  41. nes_apu_write(&sys->apu, addr, val);
  42. } else {
  43. nes_map_write(sys->cart.mapper, addr, val);
  44. }
  45. }
  46. static void nes_irq(void* sys, int active) {
  47. e6502_set_irq(&((nes*)sys)->cpu, active);
  48. }
  49. int nes_init(nes* sys, int audio_freq) {
  50. e6502_init(&sys->cpu, (e6502_Read*)nes_mem_read,
  51. (e6502_Write*)nes_mem_write, sys);
  52. nes_map_set_irq(sys->cart.mapper, nes_irq, sys);
  53. nes_ppu_init(&sys->ppu, &sys->cart);
  54. return nes_apu_init(
  55. &sys->apu,
  56. nes_clock_master_num / nes_clock_master_den,
  57. audio_freq,
  58. (uint8_t(*)(void*, uint16_t))nes_mem_read,
  59. sys
  60. );
  61. }
  62. void nes_reset(nes* sys) {
  63. e6502_reset(&sys->cpu);
  64. nes_ppu_reset(&sys->ppu);
  65. nes_apu_reset(&sys->apu);
  66. }
  67. nes_ppu_Result nes_step(nes* sys, int* run) {
  68. nes_ppu_Result result = ppu_Result_Halt;
  69. int cpu_run = e6502_step(&sys->cpu);
  70. if (cpu_run > 0) {
  71. int master_cycles = cpu_run * nes_clock_cpu_div;
  72. nes_apu_Result aresult = nes_apu_run(&sys->apu,
  73. master_cycles);
  74. // TODO: Does this conflict with MMC3?
  75. e6502_set_irq(&sys->cpu, aresult == apu_Result_IRQ);
  76. int ppu_cycles = master_cycles / nes_clock_ppu_div;
  77. result = nes_ppu_run(&sys->ppu, ppu_cycles);
  78. if (result == ppu_Result_VBlank_On) {
  79. e6502_set_nmi(&sys->cpu, 1);
  80. } else if (result == ppu_Result_VBlank_Off) {
  81. e6502_set_nmi(&sys->cpu, 0);
  82. }
  83. if (run) *run = master_cycles;
  84. }
  85. return result;
  86. }