diff --git a/devices/memctrl/hmc.cpp b/devices/memctrl/hmc.cpp
index c9e9c1e..c4d7f83 100644
--- a/devices/memctrl/hmc.cpp
+++ b/devices/memctrl/hmc.cpp
@@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
-Copyright (C) 2018-21 divingkatae and maximum
+Copyright (C) 2018-24 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@@ -27,6 +27,7 @@ along with this program. If not, see .
#include
#include
#include
+#include
HMC::HMC() : MemCtrlBase()
{
@@ -34,7 +35,7 @@ HMC::HMC() : MemCtrlBase()
supports_types(HWCompType::MEM_CTRL | HWCompType::MMIO_DEV);
- /* add memory mapped I/O region for the HMC control register */
+ // add memory mapped I/O region for the HMC control register
add_mmio_region(0x50F40000, 0x10000, this);
this->ctrl_reg = 0ULL;
@@ -46,7 +47,7 @@ uint32_t HMC::read(uint32_t rgn_start, uint32_t offset, int size)
if (!offset)
return !!(this->ctrl_reg & (1ULL << this->bit_pos++));
else
- return 0; /* FIXME: what should be returned for invalid offsets? */
+ return 0; // FIXME: what should be returned for invalid offsets?
}
void HMC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
@@ -58,13 +59,151 @@ void HMC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
bit = 1ULL << this->bit_pos++;
this->ctrl_reg = (value & 1) ? this->ctrl_reg | bit :
this->ctrl_reg & ~bit;
+ if (this->bit_pos >= HMC_CTRL_BITS) {
+ this->bit_pos = 0;
+ if (((this->ctrl_reg >> HMC_RAM_CFG_POS) & 3) != this->bank_config) {
+ this->bank_config = (this->ctrl_reg >> HMC_RAM_CFG_POS) & 3;
+ this->remap_ram_regions();
+ }
+ }
break;
- case 8: /* writing to HMCBase + 8 resets internal bit position */
+ case 8: // writing to HMCBase + 8 resets internal bit position
this->bit_pos = 0;
break;
}
}
+void HMC::remap_ram_regions() {
+ uint32_t bank_b_addr;
+
+ switch (this->bank_config & 3) {
+ case BANK_CFG_128MB:
+ bank_b_addr = BANK_B_START;
+ break;
+ case BANK_CFG_2MB:
+ bank_b_addr = this->mb_bank_size + BANK_SIZE_2MB;
+ break;
+ case BANK_CFG_8MB:
+ bank_b_addr = this->mb_bank_size + BANK_SIZE_8MB;
+ break;
+ case BANK_CFG_32MB:
+ bank_b_addr = this->mb_bank_size + BANK_SIZE_32MB;
+ break;
+ }
+
+ if (this->bank_b_size && this->bank_b_start != bank_b_addr) {
+ AddressMapEntry *ref_entry = find_range(this->bank_b_start);
+ if (ref_entry) {
+ ref_entry->end = bank_b_addr + (ref_entry->end - ref_entry->start);
+ ref_entry->start = bank_b_addr;
+
+ this->bank_b_start = bank_b_addr;
+ LOG_F(INFO, "%s: successfully relocated bank B mem region to 0x%X",
+ this->name.c_str(), bank_b_addr);
+ } else
+ LOG_F(ERROR, "%s: failed to relocate bank B mem region to 0x%X",
+ this->name.c_str(), bank_b_addr);
+ }
+}
+
+int HMC::install_ram(uint32_t mb_bank_size, uint32_t bank_a_size, uint32_t bank_b_size) {
+ if (mb_bank_size != BANK_SIZE_4MB && mb_bank_size != BANK_SIZE_8MB) {
+ LOG_F(ERROR, "%s: invalid motherboard bank size %d", this->name.c_str(),
+ mb_bank_size);
+ return -1;
+ }
+
+ if (!bank_a_size && bank_b_size) {
+ LOG_F(ERROR, "%s: bank A can't be empty while bank B is not empty",
+ this->name.c_str());
+ return -1;
+ }
+
+ if (!this->add_ram_region(BANK_MB_START, mb_bank_size)) {
+ LOG_F(ERROR, "%s: could not allocate motherboard RAM region!", this->name.c_str());
+ return -1;
+ }
+
+ this->mb_bank_start = BANK_MB_START;
+ this->mb_bank_size = mb_bank_size;
+ this->bank_a_start = -1;
+ this->bank_a_size = bank_a_size;
+ this->bank_b_start = -1;
+ this->bank_b_size = bank_b_size;
+
+ if (bank_a_size) {
+ // create alias for RAM bank A (required for memory sizing)
+ if (!this->add_ram_region(BANK_A_ALIAS, bank_a_size)) {
+ LOG_F(ERROR, "%s: could not allocate region for bank A!",
+ this->name.c_str());
+ return -1;
+ }
+
+ this->bank_a_start = BANK_A_ALIAS;
+
+ uint32_t offset = 0;
+ uint32_t size = bank_a_size;
+
+ // make the main region for the bank A starting right after
+ // the motherboard bank
+ if (bank_a_size > BANK_SIZE_120MB) {
+ // For a full 128MB bank, the lower part of this region is hidden
+ // by the motherboard bank. Set up the partial mirror!
+ offset = mb_bank_size;
+ size = BANK_SIZE_120MB;
+ }
+
+ if (!this->add_mem_mirror_partial(mb_bank_size, BANK_A_ALIAS,
+ offset, size)) {
+ LOG_F(ERROR, "%s: could not create mirror for RAM bank A!",
+ this->name.c_str());
+ return -1;
+ }
+
+ // Create additional aliases for bank A if the installed memory is
+ // smaller than 8 MB. That's because HWInit always searches those areas
+ // for the warm start signature.
+ if (bank_a_size < BANK_SIZE_8MB) {
+ for (uint32_t alias_start = BANK_B_START + bank_a_size - BANK_SIZE_8MB;
+ alias_start < BANK_B_START; alias_start += bank_a_size) {
+ if (!this->add_mem_mirror(alias_start, this->bank_a_start)) {
+ LOG_F(ERROR, "%s: could not create alias for RAM bank A!",
+ this->name.c_str());
+ return -1;
+ }
+ }
+ }
+ }
+
+ if (bank_b_size) {
+ if (!this->add_ram_region(BANK_B_START, bank_b_size)) {
+ LOG_F(ERROR, "%s: could not allocate region for bank B!",
+ this->name.c_str());
+ return -1;
+ }
+
+ this->bank_b_start = BANK_B_START;
+
+ // Create additional aliases for bank B if the installed memory is
+ // smaller than 8 MB. That's because HWInit always searches those areas
+ // for the warm start signature.
+ if (bank_b_size < BANK_SIZE_8MB) {
+ for (uint32_t alias_start = BANK_A_ALIAS + bank_b_size - BANK_SIZE_8MB;
+ alias_start < BANK_A_ALIAS; alias_start += bank_b_size) {
+ if (!this->add_mem_mirror(alias_start, this->bank_b_start)) {
+ LOG_F(ERROR, "%s: could not create alias for RAM bank B!",
+ this->name.c_str());
+ return -1;
+ }
+ }
+ }
+ }
+
+ this->remap_ram_regions();
+
+ return 0;
+}
+
static const DeviceDescription Hmc_Descriptor = {
HMC::create, {}, {}
};
diff --git a/devices/memctrl/hmc.h b/devices/memctrl/hmc.h
index ac528ca..3836657 100644
--- a/devices/memctrl/hmc.h
+++ b/devices/memctrl/hmc.h
@@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
-Copyright (C) 2018-21 divingkatae and maximum
+Copyright (C) 2018-24 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@@ -37,9 +37,28 @@ along with this program. If not, see .
#include
#include
-/** Bit in the HMC control register telling us where the frame bufffer
- for the on-board video is located. */
-#define HMC_VBASE_BIT 33
+/* Control register bit definitions. */
+enum {
+ HMC_CTRL_BITS = 35, // width of the HMC control register
+ HMC_RAM_CFG_POS = 29, // DRAM configuration bits position
+ HMC_VBASE_BIT = 33, // framebuffer base address: 0 - 0x00100000, 1 - 0x0
+};
+
+/* Motherboard RAM size constans. */
+enum {
+ BANK_CFG_128MB = 0, // 12 x 12 address maxtrix (reset config)
+ BANK_CFG_2MB = 1, // 9 x 9 address maxtrix
+ BANK_CFG_8MB = 2, // 10 x 10 address maxtrix
+ BANK_CFG_32MB = 3, // 11 x 11 address maxtrix
+ BANK_SIZE_2MB = 0x200000,
+ BANK_SIZE_4MB = 0x400000,
+ BANK_SIZE_8MB = 0x800000,
+ BANK_SIZE_32MB = 0x2000000,
+ BANK_SIZE_120MB = 0x07800000,
+ BANK_MB_START = 0x00000000, // starting address of the motherboard bank
+ BANK_B_START = 0x08000000,
+ BANK_A_ALIAS = 0x10000000,
+};
class HMC : public MemCtrlBase, public MMIODevice {
public:
@@ -54,13 +73,27 @@ public:
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
+ int install_ram(uint32_t mb_bank_size, uint32_t bank_a_size, uint32_t bank_b_size);
+
uint64_t get_control_reg(void) {
return this->ctrl_reg;
};
+protected:
+ void remap_ram_regions();
+
private:
- int bit_pos;
- uint64_t ctrl_reg;
+ int bit_pos = 0;
+ uint64_t ctrl_reg = 0;
+ uint8_t bank_config = BANK_CFG_128MB;
+
+ uint32_t mb_bank_start = -1;
+ uint32_t bank_a_start = -1;
+ uint32_t bank_b_start = -1;
+
+ uint32_t mb_bank_size = 0;
+ uint32_t bank_a_size = 0;
+ uint32_t bank_b_size = 0;
};
#endif // HMC_H