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.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

258 lignes
7.6KB

  1. #include "memory.h"
  2. #define NESE_DEBUG "MMC3"
  3. #include "log.h"
  4. typedef enum {
  5. mmc3_Flag_Horizontal = 0b00000001,
  6. mmc3_Flag_IRQ_Enabled = 0b00000010,
  7. mmc3_Flag_IRQ_Reload = 0b00000100,
  8. mmc3_Flag_CHR_RAM = 0b00001000,
  9. mmc3_Flag_Battery = 0b00100000,
  10. mmc3_Flag_WRAM_Protect = 0b01000000,
  11. mmc3_Flag_WRAM_Enabled = 0b10000000,
  12. } mmc3_Flag;
  13. typedef enum {
  14. mmc3_Bank_Select_Reg = 0b00000111,
  15. mmc3_Bank_Select_PRG = 0b01000000,
  16. mmc3_Bank_Select_CHR = 0b10000000,
  17. } mmc3_Bank_Select;
  18. typedef struct {
  19. uint8_t r[8];
  20. uint8_t flags;
  21. uint8_t bank_select;
  22. uint8_t irq_count;
  23. uint8_t irq_latch;
  24. } map004_data;
  25. static inline void mmc3_update_vram(map004_data* data,
  26. nes_Memory* mem) {
  27. nes_ppu_set_mirroring(
  28. &mem->ppu,
  29. data->flags & mmc3_Flag_Horizontal ?
  30. nes_Mirror_Horizontal : nes_Mirror_Vertical
  31. );
  32. }
  33. static inline void mmc3_map_prg(nes_Memory* mem,
  34. int bank, int page) {
  35. LOGD("PRG ROM: 8k $%04x <- bank %d", 0x8000 + (bank << 13), page);
  36. mem->rom_bank[bank] = prg_rom_page(mem,
  37. page % mem->n_rom_banks);
  38. }
  39. static inline void mmc3_update_prg(map004_data* data,
  40. nes_Memory* mem,
  41. int reg, uint8_t bank) {
  42. if (reg == 7) {
  43. mmc3_map_prg(mem, 1, bank);
  44. } else {
  45. if (!(data->bank_select & mmc3_Bank_Select_PRG)) {
  46. mmc3_map_prg(mem, 0, bank);
  47. } else {
  48. mmc3_map_prg(mem, 2, bank);
  49. }
  50. }
  51. }
  52. static inline void mmc3_map_2k_chr(nes_Memory* mem,
  53. int reg, int bank) {
  54. bank = (bank & ~1) % mem->ppu.n_chr_banks;
  55. LOGD("CHR ROM: 2k $%04x <- bank %d + %d", reg << 10, bank, bank | 1);
  56. mem->ppu.bank[reg + 0] = chr_page(&mem->ppu, bank);
  57. mem->ppu.bank[reg + 1] = chr_page(&mem->ppu, bank | 1);
  58. }
  59. static inline void mmc3_map_1k_chr(nes_Memory* mem,
  60. int reg, int bank) {
  61. bank = bank % mem->ppu.n_chr_banks;
  62. LOGD("CHR ROM: 1k $%04x <- bank %d", reg << 10, bank);
  63. mem->ppu.bank[reg] = chr_page(&mem->ppu, bank);
  64. }
  65. static inline void mmc3_update_chr(map004_data* data,
  66. nes_Memory* mem,
  67. int reg, uint8_t bank) {
  68. if (!(data->bank_select & mmc3_Bank_Select_CHR)) {
  69. if (1 >= reg) {
  70. mmc3_map_2k_chr(mem, reg * 2, bank);
  71. } else {
  72. mmc3_map_1k_chr(mem, reg + 2, bank);
  73. }
  74. } else {
  75. if (1 >= reg) {
  76. mmc3_map_2k_chr(mem, 4 + (reg * 2), bank);
  77. } else {
  78. mmc3_map_1k_chr(mem, reg - 2, bank);
  79. }
  80. }
  81. }
  82. static inline void mmc3_update_rom_mode(map004_data* data,
  83. nes_Memory* mem,
  84. uint8_t val) {
  85. uint8_t delta = (data->bank_select ^ val);
  86. data->bank_select = val;
  87. if (delta & mmc3_Bank_Select_PRG) {
  88. mmc3_map_prg(mem, 1, data->r[7]);
  89. if (!(val & mmc3_Bank_Select_PRG)) {
  90. mmc3_map_prg(mem, 0, data->r[6]);
  91. mmc3_map_prg(mem, 2, mem->n_rom_banks - 2);
  92. } else {
  93. mmc3_map_prg(mem, 0, mem->n_rom_banks - 2);
  94. mmc3_map_prg(mem, 2, data->r[6]);
  95. }
  96. }
  97. if (delta & mmc3_Bank_Select_CHR) {
  98. mmc3_update_chr(data, mem, 0, data->r[0]);
  99. mmc3_update_chr(data, mem, 1, data->r[1]);
  100. mmc3_update_chr(data, mem, 2, data->r[2]);
  101. mmc3_update_chr(data, mem, 3, data->r[3]);
  102. mmc3_update_chr(data, mem, 4, data->r[4]);
  103. mmc3_update_chr(data, mem, 5, data->r[5]);
  104. }
  105. }
  106. static void map004_reset(nes_Mapper* map, nes_Memory* mem) {
  107. map004_data* data = (map004_data*)map->data;
  108. data->irq_count = 0;
  109. data->irq_latch = 0;
  110. mmc3_update_rom_mode(data, mem, 0);
  111. mmc3_map_prg(mem, 3, mem->n_rom_banks - 1);
  112. mmc3_update_vram(data, mem);
  113. }
  114. static int map004_init(nes_Mapper* map, const ines_Header* hdr,
  115. nes_Memory* mem) {
  116. map004_data* data = (map004_data*)map->data;
  117. data->flags = (hdr->flags_6 & ines_Flag_Vert_Mirror) ?
  118. mmc3_Flag_Horizontal : 0;
  119. data->bank_select = mmc3_Bank_Select_PRG |
  120. mmc3_Bank_Select_CHR;
  121. map004_reset(map, mem);
  122. return 0;
  123. }
  124. static int map004_protect_sram(nes_Mapper* map, nes_Memory* mem,
  125. uint16_t addr, uint8_t val) {
  126. // Intercept any writes!
  127. return 0;
  128. }
  129. static int map004_write_rom(nes_Mapper* map, nes_Memory* mem,
  130. uint16_t addr, uint8_t val) {
  131. map004_data* data = (map004_data*)map->data;
  132. LOGD("$%04x < %02x", (int)addr, (int)val);
  133. switch ((addr >> 13) & 3) {
  134. case 0: // 0x8000 - 0x9FFF
  135. if (addr & 1) {
  136. // Bank data
  137. int reg = (data->bank_select & mmc3_Bank_Select_Reg);
  138. if (reg >= 6) {
  139. mmc3_update_prg(data, mem, reg, val);
  140. } else {
  141. mmc3_update_chr(data, mem, reg, val);
  142. }
  143. data->r[reg] = val;
  144. } else {
  145. // Bank Select
  146. mmc3_update_rom_mode(data, mem, val);
  147. }
  148. break;
  149. case 1: // 0xA000 - 0xBFFF
  150. if (addr & 1) {
  151. LOGD("SRAM %s, %s", (val & mmc3_Flag_WRAM_Enabled) ? "enabled" : "disabled", (val & mmc3_Flag_WRAM_Protect) ? "protected" : "writable");
  152. // WRAM protection
  153. data->flags &= ~(mmc3_Flag_WRAM_Enabled |
  154. mmc3_Flag_WRAM_Protect);
  155. data->flags |= (val & (mmc3_Flag_WRAM_Enabled |
  156. mmc3_Flag_WRAM_Protect));
  157. // TODO: Set these on load / reset
  158. if (val & mmc3_Flag_WRAM_Enabled) {
  159. mem->sram_bank = mem->sram;
  160. } else {
  161. mem->sram_bank = NULL;
  162. }
  163. if (val & mmc3_Flag_WRAM_Protect) {
  164. map->write_sram = map004_protect_sram;
  165. } else {
  166. map->write_sram = NULL;
  167. }
  168. } else {
  169. // Mirroring
  170. data->flags &= ~mmc3_Flag_Horizontal;
  171. data->flags |= (val & mmc3_Flag_Horizontal);
  172. mmc3_update_vram(data, mem);
  173. }
  174. break;
  175. case 2: // 0xC000 - 0xDFFF
  176. if (addr & 1) {
  177. LOGD("IRQ Reload");
  178. data->flags |= mmc3_Flag_IRQ_Reload;
  179. } else {
  180. LOGD("IRQ Latch: %d", val);
  181. data->irq_latch = val;
  182. }
  183. break;
  184. case 3: // 0xE000 - 0xFFFF
  185. LOGD("IRQ %s", (addr & 1) ? "Enable" : "Disable");
  186. if (addr & 1) {
  187. data->flags |= mmc3_Flag_IRQ_Enabled;
  188. } else {
  189. data->flags &= ~mmc3_Flag_IRQ_Enabled;
  190. map->irq_callback(map->irq_arg, 0);
  191. }
  192. break;
  193. }
  194. return 0;
  195. }
  196. static int map004_hsync(nes_Mapper* map) {
  197. map004_data* data = (map004_data*)map->data;
  198. if ( data->irq_count <= 0 ||
  199. (data->flags & mmc3_Flag_IRQ_Reload)) {
  200. data->irq_count = data->irq_latch;
  201. data->flags &= ~mmc3_Flag_IRQ_Reload;
  202. } else {
  203. data->irq_count--;
  204. }
  205. if ( data->irq_count <= 0 &&
  206. (data->flags & mmc3_Flag_IRQ_Enabled)) {
  207. LOGD("IRQ Trigger");
  208. map->irq_callback(map->irq_arg, 1);
  209. data->irq_count = 0;
  210. }
  211. return 0;
  212. }
  213. const nes_Mapper map004 = {
  214. .name = "MMC3",
  215. .max_chr_banks = 256,
  216. .data_size = sizeof(map004_data),
  217. .init = map004_init,
  218. .reset = map004_reset,
  219. .write_rom = map004_write_rom,
  220. .hsync = map004_hsync,
  221. };