An emulator, assembler, and disassembler for the Sega Game Gear
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.

mmu.c 4.8 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include "logging.h"
  6. #include "z80.h"
  7. /*
  8. Initialize a MMU object. This must be called before using the MMU.
  9. Return true if initialization was successful, or false if the required
  10. amount of memory could not be allocated.
  11. */
  12. bool mmu_init(MMU *mmu)
  13. {
  14. mmu->system_ram = malloc(sizeof(uint8_t) * MMU_SYSTEM_RAM_SIZE);
  15. if (!mmu->system_ram)
  16. return false;
  17. for (size_t slot = 0; slot < MMU_NUM_SLOTS; slot++)
  18. mmu->map_slots[slot] = NULL;
  19. for (size_t bank = 0; bank < MMU_NUM_ROM_BANKS; bank++)
  20. mmu->rom_banks[bank] = NULL;
  21. return true;
  22. }
  23. /*
  24. Free memory previously allocated by the MMU.
  25. */
  26. void mmu_free(MMU *mmu)
  27. {
  28. free(mmu->system_ram);
  29. }
  30. /*
  31. Load a block of ROM into the MMU.
  32. size must be a multiple of MMU_ROM_BANK_SIZE (16 KB), the load will fail
  33. silently. It should also be a power of two, or problems might occur with
  34. ROM mirroring logic. It should not be larger than
  35. MMU_ROM_BANK_SIZE * MMU_NUM_ROM_BANKS, or the extra banks will be ignored.
  36. This function will still work if called while the system is running, but it
  37. will likely cause unexpected behavior.
  38. */
  39. void mmu_load_rom(MMU *mmu, const uint8_t *data, size_t size)
  40. {
  41. if (size % MMU_ROM_BANK_SIZE)
  42. return;
  43. size_t banks = size / MMU_ROM_BANK_SIZE, bank, mirror;
  44. if (banks > MMU_NUM_ROM_BANKS)
  45. banks = MMU_NUM_ROM_BANKS;
  46. for (bank = 0; bank < banks; bank++) {
  47. for (mirror = bank; mirror < MMU_NUM_ROM_BANKS; mirror += banks)
  48. mmu->rom_banks[mirror] = data + (bank * MMU_ROM_BANK_SIZE);
  49. }
  50. #ifdef DEBUG_MODE
  51. char temp_str[64];
  52. DEBUG("Dumping MMU bank table:")
  53. for (size_t group = 0; group < MMU_NUM_ROM_BANKS / 8; group++) {
  54. for (size_t elem = 0; elem < 8; elem++) {
  55. bank = 8 * group + elem;
  56. snprintf(temp_str + 6 * elem, 7, "%02zX=%02zX ", bank,
  57. (mmu->rom_banks[bank] - data) >> 14);
  58. }
  59. temp_str[47] = '\0';
  60. DEBUG("- %s", temp_str)
  61. }
  62. #endif
  63. }
  64. /*
  65. Map the given RAM slot to the given ROM bank.
  66. */
  67. static inline void map_slot(MMU *mmu, size_t slot, size_t bank)
  68. {
  69. DEBUG("MMU mapping memory slot %zu to bank 0x%02zX", slot, bank)
  70. mmu->map_slots[slot] = mmu->rom_banks[bank];
  71. }
  72. /*
  73. Power on the MMU, setting initial memory values.
  74. This must be called before memory is read from or written to. If no ROM has
  75. been loaded, those regions will be read as 0xFF and will not accept writes.
  76. */
  77. void mmu_power(MMU *mmu)
  78. {
  79. for (size_t slot = 0; slot < MMU_NUM_SLOTS; slot++)
  80. map_slot(mmu, slot, slot);
  81. memset(mmu->system_ram, 0xFF, MMU_SYSTEM_RAM_SIZE);
  82. }
  83. /*
  84. Read a byte from a memory bank, or return 0xFF if the bank is not mapped.
  85. */
  86. static inline uint8_t bank_byte_read(const uint8_t* bank, uint16_t addr)
  87. {
  88. return bank ? bank[addr] : 0xFF;
  89. }
  90. /*
  91. Read a byte of memory from the given address.
  92. Memory region information is based on:
  93. - http://www.smspower.org/Development/MemoryMap
  94. - http://www.smspower.org/Development/Mappers
  95. */
  96. uint8_t mmu_read_byte(MMU *mmu, uint16_t addr)
  97. {
  98. if (addr < 0x0400) // First kilobyte is unpaged, for interrupt handlers
  99. return bank_byte_read(mmu->rom_banks[0], addr);
  100. else if (addr < 0x4000) // Slot 0 (0x0400 - 0x3FFF)
  101. return bank_byte_read(mmu->map_slots[0], addr);
  102. else if (addr < 0x8000) // Slot 1 (0x4000 - 0x7FFF)
  103. return bank_byte_read(mmu->map_slots[1], addr - 0x4000);
  104. else if (addr < 0xC000) // Slot 2 (0x8000 - 0xBFFF)
  105. return bank_byte_read(mmu->map_slots[2], addr - 0x8000);
  106. else if (addr < 0xE000) // System RAM (0xC000 - 0xDFFF)
  107. return mmu->system_ram[addr - 0xC000];
  108. else // System RAM, mirrored (0xE000 - 0xFFFF)
  109. return mmu->system_ram[addr - 0xE000];
  110. }
  111. /*
  112. Write a byte of memory to the given address.
  113. Return true if the byte was written, and false if it wasn't. Writes will
  114. fail when attempting to write to read-only memory.
  115. */
  116. bool mmu_write_byte(MMU *mmu, uint16_t addr, uint8_t value)
  117. {
  118. if (addr < 0xC000) { // TODO: implement writes to on-cartridge RAM
  119. return false;
  120. } else if (addr < 0xE000) { // System RAM (0xC000 - 0xDFFF)
  121. mmu->system_ram[addr - 0xC000] = value;
  122. return true;
  123. } else { // System RAM, mirrored (0xE000 - 0xFFFF)
  124. if (addr == 0xFFFC) {
  125. // TODO: handle cartridge RAM mapping control
  126. } else if (addr == 0xFFFD)
  127. map_slot(mmu, 0, value & 0x3F);
  128. else if (addr == 0xFFFE)
  129. map_slot(mmu, 1, value & 0x3F);
  130. else if (addr == 0xFFFF)
  131. map_slot(mmu, 2, value & 0x3F);
  132. mmu->system_ram[addr - 0xE000] = value;
  133. return true;
  134. }
  135. }