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.

258 líneas
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. };