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.

154 líneas
4.6KB

  1. #include "memory.h"
  2. //#define NESE_DEBUG "MMC1"
  3. #include "log.h"
  4. typedef struct {
  5. uint8_t reg_shift;
  6. uint8_t reg_n_shift;
  7. uint8_t reg_control;
  8. uint8_t reg_chr_0;
  9. uint8_t reg_chr_1;
  10. uint8_t reg_prg;
  11. } map001_data;
  12. static inline void mmc1_update_vram(map001_data* data,
  13. nes_Memory* mem) {
  14. const nes_Nametable_Mirroring mirror[4] = {
  15. nes_Mirror_Only_0,
  16. nes_Mirror_Only_1,
  17. nes_Mirror_Vertical,
  18. nes_Mirror_Horizontal,
  19. };
  20. LOGD("Mirroring %d", mirror[data->reg_control & 0b11]);
  21. nes_ppu_set_mirroring(&mem->ppu,
  22. mirror[data->reg_control & 0b11]);
  23. }
  24. static inline void mmc1_update_chr(map001_data* data,
  25. nes_Memory* mem) {
  26. int n_banks = mem->ppu.n_chr_banks / 4;
  27. int bank_0 = 0, bank_1 = 0;
  28. if (!(data->reg_control & 0b10000)) {
  29. bank_0 = (data->reg_chr_0 & 0b11110) % n_banks;
  30. bank_1 = bank_0 + 1;
  31. } else {
  32. bank_0 = data->reg_chr_0 % n_banks;
  33. bank_1 = data->reg_chr_1 % n_banks;
  34. }
  35. LOGD("CHR: %d + %d", bank_0, bank_1);
  36. mem->ppu.bank[0] = chr_page(&mem->ppu, (bank_0 * 4) + 0);
  37. mem->ppu.bank[1] = chr_page(&mem->ppu, (bank_0 * 4) + 1);
  38. mem->ppu.bank[2] = chr_page(&mem->ppu, (bank_0 * 4) + 2);
  39. mem->ppu.bank[3] = chr_page(&mem->ppu, (bank_0 * 4) + 3);
  40. mem->ppu.bank[4] = chr_page(&mem->ppu, (bank_1 * 4) + 0);
  41. mem->ppu.bank[5] = chr_page(&mem->ppu, (bank_1 * 4) + 1);
  42. mem->ppu.bank[6] = chr_page(&mem->ppu, (bank_1 * 4) + 2);
  43. mem->ppu.bank[7] = chr_page(&mem->ppu, (bank_1 * 4) + 3);
  44. }
  45. static inline void mmc1_update_prg(map001_data* data,
  46. nes_Memory* mem) {
  47. int n_banks = mem->n_rom_banks / 2;
  48. int mode = (data->reg_control >> 2) & 3;
  49. int bank_0 = (data->reg_prg & 0b01111) % n_banks;
  50. int bank_1 = bank_0;
  51. if (!(mode & 0b10)) {
  52. bank_0 = (bank_0 & 0b01110);
  53. bank_1 = bank_0 + 1;
  54. } else if (mode == 2) {
  55. bank_0 = 0;
  56. } else if (mode == 3) {
  57. bank_1 = n_banks - 1;
  58. }
  59. LOGD("PRG: %d + %d", bank_0, bank_1);
  60. mem->rom_bank[0] = prg_rom_page(mem, (bank_0 * 2) + 0);
  61. mem->rom_bank[1] = prg_rom_page(mem, (bank_0 * 2) + 1);
  62. mem->rom_bank[2] = prg_rom_page(mem, (bank_1 * 2) + 0);
  63. mem->rom_bank[3] = prg_rom_page(mem, (bank_1 * 2) + 1);
  64. }
  65. static void map001_reset(nes_Mapper* map, nes_Memory* mem) {
  66. map001_data* data = (map001_data*)map->data;
  67. data->reg_shift = 0b10000;
  68. data->reg_control = 0b01100;
  69. data->reg_chr_0 = 0;
  70. data->reg_chr_1 = 0;
  71. data->reg_prg = 0;
  72. mmc1_update_prg(data, mem);
  73. mmc1_update_chr(data, mem);
  74. mmc1_update_vram(data, mem);
  75. }
  76. static int map001_init(nes_Mapper* map, const ines_Header* hdr,
  77. nes_Memory* mem) {
  78. map001_reset(map, mem);
  79. return 0;
  80. }
  81. static int map001_write_rom(nes_Mapper* map, nes_Memory* mem,
  82. uint16_t addr, uint8_t val) {
  83. map001_data* data = (map001_data*)map->data;
  84. LOGD("Write $%04x < %02x", addr, val);
  85. if (val & 0x80U) {
  86. data->reg_shift = 0b10000;
  87. data->reg_control |= 0b01100;
  88. mmc1_update_prg(data, mem);
  89. } else {
  90. // TODO: Handle consecutive-cycle writes?
  91. int done = (data->reg_shift & 1);
  92. data->reg_shift = (data->reg_shift >> 1) |
  93. ((val & 1) << 4);
  94. if (done) {
  95. switch ((addr >> 13) & 3) {
  96. case 0:
  97. LOGD("Control %02x", data->reg_shift);
  98. data->reg_control = data->reg_shift;
  99. mmc1_update_chr(data, mem);
  100. mmc1_update_vram(data, mem);
  101. mmc1_update_prg(data, mem);
  102. break;
  103. case 1:
  104. LOGD("CHR_0 %02x", data->reg_shift);
  105. data->reg_chr_0 = data->reg_shift;
  106. mmc1_update_chr(data, mem);
  107. break;
  108. case 2:
  109. LOGD("CHR_1 %02x", data->reg_shift);
  110. data->reg_chr_1 = data->reg_shift;
  111. mmc1_update_chr(data, mem);
  112. break;
  113. case 3:
  114. LOGD("PRG %02x", data->reg_shift);
  115. data->reg_prg = data->reg_shift;
  116. mmc1_update_prg(data, mem);
  117. break;
  118. }
  119. data->reg_shift = 0b10000;
  120. }
  121. }
  122. return 0;
  123. }
  124. const nes_Mapper map001 = {
  125. .name = "MMC1",
  126. .max_chr_banks = 128,
  127. .data_size = sizeof(map001_data),
  128. .init = map001_init,
  129. .reset = map001_reset,
  130. .write_rom = map001_write_rom,
  131. };