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.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 line
12KB

  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include "map.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* prg_rom;
  20. int prg_rom_banks; // 16 KB / 8 KB = 2
  21. uint8_t* chr_rom;
  22. int chr_rom_banks; // 4 KB / 1 KB = 4
  23. nes_mapper* mapper;
  24. uint8_t* prg_bank[4]; // 8 KB
  25. uint8_t* chr_bank[8]; // 1 KB
  26. uint8_t* vram_bank[4];
  27. int chr_bank_offset[8];
  28. int chr_ram_lim;
  29. uint8_t r[8];
  30. uint8_t flags;
  31. uint8_t bank_select;
  32. uint8_t irq_count;
  33. uint8_t irq_latch;
  34. uint8_t wram[nes_mem_wram_size];
  35. uint8_t vram[2][nes_vram_page_size];
  36. uint8_t chr_ram[];
  37. } mmc3_mapper;
  38. static inline uint8_t* mmc3_prg_bank(mmc3_mapper* map, int bank) {
  39. return &map->prg_rom[(bank % map->prg_rom_banks) << 13];
  40. }
  41. static inline void mmc3_map_prg(mmc3_mapper* map,
  42. int reg, int bank) {
  43. MAP_LOG("PRG ROM: 8k $%04x <- bank %d", reg << 13, bank);
  44. map->prg_bank[reg] = mmc3_prg_bank(map, bank);
  45. }
  46. static inline void mmc3_update_prg(mmc3_mapper* map,
  47. int reg, uint8_t bank) {
  48. if (reg == 7) {
  49. mmc3_map_prg(map, 1, bank);
  50. } else {
  51. if (!(map->bank_select & mmc3_Bank_Select_PRG)) {
  52. mmc3_map_prg(map, 0, bank);
  53. } else {
  54. mmc3_map_prg(map, 2, bank);
  55. }
  56. }
  57. }
  58. static inline uint8_t* mmc3_chr_bank(mmc3_mapper* map, int bank) {
  59. return &map->chr_rom[(bank % map->chr_rom_banks) << 10];
  60. }
  61. static inline void mmc3_map_2k_chr(mmc3_mapper* map,
  62. int reg, int bank) {
  63. bank &= ~1;
  64. if (bank >= map->chr_rom_banks) {
  65. MAP_LOG("CHR ROM OOB: %d > %d", bank, map->chr_rom_banks);
  66. bank = bank % map->chr_rom_banks;
  67. }
  68. MAP_LOG("CHR ROM: 2k $%04x <- bank %d + %d", reg << 10, bank, bank | 1);
  69. map->chr_bank[reg + 0] = mmc3_chr_bank(map, bank);
  70. map->chr_bank[reg + 1] = mmc3_chr_bank(map, bank | 1);
  71. map->chr_bank_offset[reg + 0] = map->chr_bank[reg + 0] - map->chr_rom;
  72. map->chr_bank_offset[reg + 1] = map->chr_bank[reg + 1] - map->chr_rom;
  73. }
  74. static inline void mmc3_map_1k_chr(mmc3_mapper* map,
  75. int reg, int bank) {
  76. if (bank >= map->chr_rom_banks) {
  77. MAP_LOG("CHR ROM OOB: %d > %d", bank, map->chr_rom_banks);
  78. bank = bank % map->chr_rom_banks;
  79. }
  80. MAP_LOG("CHR ROM: 1k $%04x <- bank %d", reg << 10, bank);
  81. map->chr_bank[reg] = mmc3_chr_bank(map, bank);
  82. map->chr_bank_offset[reg] = map->chr_bank[reg] - map->chr_rom;
  83. }
  84. static inline void mmc3_update_chr(mmc3_mapper* map,
  85. int reg, uint8_t bank) {
  86. if (!(map->bank_select & mmc3_Bank_Select_CHR)) {
  87. if (1 >= reg) {
  88. mmc3_map_2k_chr(map, reg * 2, bank);
  89. } else {
  90. mmc3_map_1k_chr(map, reg + 2, bank);
  91. }
  92. } else {
  93. if (1 >= reg) {
  94. mmc3_map_2k_chr(map, 4 + (reg * 2), bank);
  95. } else {
  96. mmc3_map_1k_chr(map, reg - 2, bank);
  97. }
  98. }
  99. }
  100. static inline void mmc3_update_rom_mode(mmc3_mapper* map, int val) {
  101. uint8_t delta = (map->bank_select ^ val);
  102. map->bank_select = val;
  103. if (delta & mmc3_Bank_Select_PRG) {
  104. mmc3_map_prg(map, 1, map->r[7]);
  105. if (!(val & mmc3_Bank_Select_PRG)) {
  106. mmc3_map_prg(map, 0, map->r[6]);
  107. mmc3_map_prg(map, 2, map->prg_rom_banks - 2);
  108. } else {
  109. mmc3_map_prg(map, 0, map->prg_rom_banks - 2);
  110. mmc3_map_prg(map, 2, map->r[6]);
  111. }
  112. }
  113. if (delta & mmc3_Bank_Select_CHR) {
  114. mmc3_update_chr(map, 0, map->r[0]);
  115. mmc3_update_chr(map, 1, map->r[1]);
  116. mmc3_update_chr(map, 2, map->r[2]);
  117. mmc3_update_chr(map, 3, map->r[3]);
  118. mmc3_update_chr(map, 4, map->r[4]);
  119. mmc3_update_chr(map, 5, map->r[5]);
  120. }
  121. }
  122. static inline void mmc3_update_vram(mmc3_mapper* map) {
  123. if (!(map->flags & mmc3_Flag_Horizontal)) {
  124. // Vertical mirroring
  125. MAP_LOG("Vertical mirroring");
  126. map->vram_bank[0] = map->vram_bank[2] = map->vram[0];
  127. map->vram_bank[1] = map->vram_bank[3] = map->vram[1];
  128. } else {
  129. // Horizontal mirroring
  130. MAP_LOG("Horizontal mirroring");
  131. map->vram_bank[0] = map->vram_bank[1] = map->vram[0];
  132. map->vram_bank[2] = map->vram_bank[3] = map->vram[1];
  133. }
  134. }
  135. static void mmc3_reset(void* data) {
  136. mmc3_mapper* map = (mmc3_mapper*)data;
  137. map->irq_count = 0;
  138. map->irq_latch = 0;
  139. mmc3_update_rom_mode(map, 0);
  140. mmc3_map_prg(map, 3, map->prg_rom_banks - 1);
  141. mmc3_update_vram(map);
  142. }
  143. static void* mmc3_init(nes_mapper* nes_map, nes_cart* cart) {
  144. int chr_ram_size = ( cart->chr_rom_banks <= 0 ?
  145. (256 * 1024) : 0);
  146. mmc3_mapper* map = calloc(1, sizeof(mmc3_mapper) +
  147. chr_ram_size);
  148. if (NULL != map) {
  149. map->mapper = nes_map;
  150. map->flags = (cart->flags & Cart_Flag_Horizontal) ?
  151. mmc3_Flag_Horizontal : 0;
  152. map->prg_rom = cart->prg_rom;
  153. map->prg_rom_banks = cart->prg_rom_banks * 2;
  154. if (cart->chr_rom_banks <= 0) {
  155. map->chr_rom = map->chr_ram;
  156. map->chr_rom_banks = 256;
  157. map->flags |= mmc3_Flag_CHR_RAM;
  158. } else {
  159. map->chr_rom = cart->chr_rom;
  160. map->chr_rom_banks = cart->chr_rom_banks * 4;
  161. }
  162. if (cart->flags & Cart_Flag_Battery) {
  163. map->flags |= mmc3_Flag_Battery;
  164. }
  165. map->bank_select = mmc3_Bank_Select_PRG |
  166. mmc3_Bank_Select_CHR;
  167. mmc3_reset(map);
  168. }
  169. return map;
  170. }
  171. static void mmc3_done(void* data) {
  172. free(data);
  173. }
  174. static inline uint8_t* mmc3_prg_addr(mmc3_mapper* map,
  175. uint16_t addr) {
  176. return &(map->prg_bank[(addr >> 13) & 3][addr & 0x1FFFU]);
  177. }
  178. static inline uint8_t* mmc3_wram_addr(mmc3_mapper* map,
  179. uint16_t addr) {
  180. return &(map->wram[addr & 0x1FFFU]);
  181. }
  182. static uint8_t mmc3_read(void* data, uint16_t addr) {
  183. uint8_t* ptr = NULL;
  184. mmc3_mapper* map = (mmc3_mapper*)data;
  185. if (addr >= nes_mem_rom_start) {
  186. ptr = mmc3_prg_addr(map, addr);
  187. } else if ( addr >= nes_mem_wram_start &&
  188. (map->flags & mmc3_Flag_WRAM_Enabled)) {
  189. ptr = mmc3_wram_addr(map, addr);
  190. // MAP_LOG("WRAM: $%04x > %02x", addr, *ptr);
  191. }
  192. uint8_t val = (NULL == ptr ? 0 : *ptr);
  193. // MAP_LOG("$%04x -> %04lx > %02x", addr, ptr - map->prg_rom, val);
  194. return val;
  195. }
  196. static void mmc3_write(void* data,
  197. uint16_t addr, uint8_t val) {
  198. mmc3_mapper* map = (mmc3_mapper*)data;
  199. if (addr >= nes_mem_rom_start) MAP_LOG("$%04x < %02x", addr, val);
  200. if (addr < nes_mem_wram_start) {
  201. // Nothing prior to WRAM
  202. } else if (addr < nes_mem_rom_start) {
  203. if ( (map->flags & mmc3_Flag_WRAM_Enabled) &&
  204. !(map->flags & mmc3_Flag_WRAM_Protect)) {
  205. // MAP_LOG("WRAM: $%04x < %02x", addr, val);
  206. *(mmc3_wram_addr(map, addr)) = val;
  207. }
  208. } else if (addr < 0xA000U) {
  209. if (addr & 1) {
  210. // Bank data
  211. int reg = (map->bank_select & mmc3_Bank_Select_Reg);
  212. if (reg >= 6) {
  213. mmc3_update_prg(map, reg, val);
  214. } else {
  215. mmc3_update_chr(map, reg, val);
  216. }
  217. map->r[reg] = val;
  218. } else {
  219. // Bank select
  220. mmc3_update_rom_mode(map, val);
  221. }
  222. } else if (addr < 0xC000U) {
  223. if (addr & 1) {
  224. MAP_LOG("WRAM %s, %s", (val & mmc3_Flag_WRAM_Enabled) ? "enabled" : "disabled", (val & mmc3_Flag_WRAM_Protect) ? "protected" : "writable");
  225. // WRAM protection
  226. map->flags &= ~(mmc3_Flag_WRAM_Enabled |
  227. mmc3_Flag_WRAM_Protect);
  228. map->flags |= (val & (mmc3_Flag_WRAM_Enabled |
  229. mmc3_Flag_WRAM_Protect));
  230. } else {
  231. // Mirroring
  232. map->flags &= ~mmc3_Flag_Horizontal;
  233. map->flags |= (val & mmc3_Flag_Horizontal);
  234. mmc3_update_vram(map);
  235. }
  236. } else if (addr < 0xE000U) {
  237. if (addr & 1) {
  238. MAP_LOG("IRQ Reload");
  239. map->flags |= mmc3_Flag_IRQ_Reload;
  240. // map->irq_count = 0;
  241. } else {
  242. MAP_LOG("IRQ Latch: %d", val);
  243. map->irq_latch = val;
  244. }
  245. } else {
  246. MAP_LOG("IRQ %s", (addr & 1) ? "Enable" : "Disable");
  247. if (addr & 1) {
  248. map->flags |= mmc3_Flag_IRQ_Enabled;
  249. } else {
  250. map->flags &= ~mmc3_Flag_IRQ_Enabled;
  251. nes_map_trigger_irq(map->mapper, 0);
  252. }
  253. }
  254. }
  255. static void mmc3_scanline(void* data) {
  256. mmc3_mapper* map = (mmc3_mapper*)data;
  257. if ( map->irq_count <= 0 ||
  258. (map->flags & mmc3_Flag_IRQ_Reload)) {
  259. map->irq_count = map->irq_latch;
  260. map->flags &= ~mmc3_Flag_IRQ_Reload;
  261. } else {
  262. map->irq_count--;
  263. }
  264. if ( map->irq_count <= 0 &&
  265. (map->flags & mmc3_Flag_IRQ_Enabled)) {
  266. MAP_LOG("IRQ Trigger");
  267. nes_map_trigger_irq(map->mapper, 1);
  268. map->irq_count = 0;
  269. }
  270. }
  271. static uint8_t* mmc3_chr_addr(void* data,
  272. uint16_t addr) {
  273. mmc3_mapper* map = (mmc3_mapper*)data;
  274. return &map->chr_bank[(addr >> 10) & 7][addr & 0x3FFU];
  275. }
  276. static uint8_t* mmc3_vram_addr(void* data,
  277. uint16_t addr) {
  278. mmc3_mapper* map = (mmc3_mapper*)data;
  279. return &map->vram_bank[(addr >> 10) & 3][addr & 0x3FFU];
  280. }
  281. static void mmc3_chr_write(void* data,
  282. uint16_t addr, uint8_t val) {
  283. mmc3_mapper* map = (mmc3_mapper*)data;
  284. if (map->flags & mmc3_Flag_CHR_RAM) {
  285. uint8_t* ptr = mmc3_chr_addr(data, addr);
  286. int pos = (ptr - map->chr_ram);
  287. if (pos >= map->chr_ram_lim) map->chr_ram_lim = pos + 1;
  288. *ptr = val;
  289. }
  290. // MAP_LOG("CHR ROM Write: $%04x < %02x\n", addr, val);
  291. }
  292. static void* mmc3_sram(void* data) {
  293. mmc3_mapper* map = (mmc3_mapper*)data;
  294. return ( (map->flags & mmc3_Flag_Battery) ?
  295. map->wram : NULL);
  296. }
  297. static int mmc3_sram_size(void* data) {
  298. mmc3_mapper* map = (mmc3_mapper*)data;
  299. return ( (map->flags & mmc3_Flag_Battery) ?
  300. sizeof(map->wram) : 0);
  301. }
  302. /* Save State */
  303. static inline int mmc3_chr_ram_size(const mmc3_mapper* map) {
  304. return ( (map->flags & mmc3_Flag_CHR_RAM) ?
  305. (256 * 1024) : 0);
  306. }
  307. static int mmc3_state_size(const void* data) {
  308. const mmc3_mapper* map = (mmc3_mapper*)data;
  309. return ( (map->wram - map->r) +
  310. sizeof(map->wram) +
  311. sizeof(map->vram) +
  312. map->chr_ram_lim);
  313. }
  314. static int mmc3_state_read(void* _map, const void* data,
  315. int data_len) {
  316. mmc3_mapper* map = (mmc3_mapper*)_map;
  317. int base_size = mmc3_state_size(map) - map->chr_ram_lim;
  318. int size = base_size + mmc3_chr_ram_size(map);
  319. if (size > data_len) size = data_len;
  320. map->chr_ram_lim = data_len - base_size;
  321. memcpy(map->r, data, size);
  322. uint8_t new_bank_select = map->bank_select;
  323. map->bank_select = ~new_bank_select;
  324. mmc3_update_rom_mode(map, new_bank_select);
  325. mmc3_update_vram(map);
  326. return size;
  327. }
  328. static int mmc3_state_write(const void* _map, void* data,
  329. int data_len) {
  330. mmc3_mapper* map = (mmc3_mapper*)_map;
  331. int size = mmc3_state_size(_map);
  332. if (size > data_len) size = data_len;
  333. memcpy(data, map->r, size);
  334. return size;
  335. }
  336. nes_mapper mapper_mmc3 = {
  337. .name = "MMC3",
  338. .init = mmc3_init,
  339. .reset = mmc3_reset,
  340. .done = mmc3_done,
  341. .read = mmc3_read,
  342. .write = mmc3_write,
  343. .chr_addr = mmc3_chr_addr,
  344. .vram_addr = mmc3_vram_addr,
  345. .chr_write = mmc3_chr_write,
  346. .scanline = mmc3_scanline,
  347. .sram_size = mmc3_sram_size,
  348. .sram = mmc3_sram,
  349. .state_size = mmc3_state_size,
  350. .state_read = mmc3_state_read,
  351. .state_write = mmc3_state_write,
  352. };