diff --git a/devices/spdram.h b/devices/spdram.h
new file mode 100644
index 0000000..c36d6f3
--- /dev/null
+++ b/devices/spdram.h
@@ -0,0 +1,141 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-20 divingkatae and maximum
+ (theweirdo) spatium
+
+(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+/** @file Serial presence detect (SPD) RAM module emulation.
+
+ Author: Max Poliakovski
+
+ @description
+ Serial presence detect (SPD) is a standard that prescribes an automatic way
+ to supply various information about a memory module.
+ A SPD-compatible memory module contains a small EEPROM that stores useful
+ information like memory size, configuration, speed, manufacturer etc.
+ The content depends on the module type.
+
+ This EEPROM is accessible via the I2C bus. The EEPROM I2C address will be
+ configured depending on the physical RAM slot the module is inserted to.
+
+ A 168-Pin SDRAM slot includes three specialized pins SA0-SA2 for setting
+ an unique I2C address for each module EEPROM. The SA0-SA2 pins are hardwired
+ differently for each RAM slot. Example:
+
+ Slot SA0-SA2 I2C address
+ A %000 0x50 + %000 = 0x50
+ B %001 0x50 + %001 = 0x51
+
+ Further reading: https://en.wikipedia.org/wiki/Serial_presence_detect
+ */
+
+#ifndef SPD_EEPROM_H
+#define SPD_EEPROM_H
+
+#include
+#include
+#include
+#include
+#include "i2c.h"
+#include "hwcomponent.h"
+
+enum RAMType : int {
+ SDRAM = 4
+};
+
+
+class SpdSdram168 : public HWComponent, public I2CDevice {
+public:
+ SpdSdram168(uint8_t addr) {
+ this->dev_addr = addr;
+ this->pos = 0;
+ };
+
+ ~SpdSdram168() = default;
+
+ bool supports_type(HWCompType type) { return type == HWCompType::RAM; };
+
+ void set_capacity(int capacity_megs) {
+ switch(capacity_megs) {
+ case 32:
+ this->eeprom_data[3] = 0xC; /* 12 rows */
+ this->eeprom_data[4] = 0x8; /* 8 columns */
+ this->eeprom_data[5] = 0x1; /* one bank */
+ break;
+ case 64:
+ this->eeprom_data[3] = 0xC; /* 12 rows */
+ this->eeprom_data[4] = 0x9; /* 9 columns */
+ this->eeprom_data[5] = 0x1; /* one bank */
+ break;
+ case 128:
+ this->eeprom_data[3] = 0xC; /* 12 rows */
+ this->eeprom_data[4] = 0xA; /* 10 columns */
+ this->eeprom_data[5] = 0x1; /* one bank */
+ break;
+ case 256:
+ this->eeprom_data[3] = 0xC; /* 12 rows */
+ this->eeprom_data[4] = 0xA; /* 10 columns */
+ this->eeprom_data[5] = 0x2; /* two banks */
+ break;
+ default:
+ throw std::invalid_argument(std::string("Unsupported capacity!"));
+ }
+ LOG_F(INFO, "SDRAM capacity set to %dMB, I2C addr = 0x%X",
+ capacity_megs, this->dev_addr);
+ };
+
+ void start_transaction() { this->pos = 0; };
+
+ bool send_subaddress(uint8_t sub_addr) {
+ this->pos = sub_addr;
+ LOG_F(9, "SDRAM subaddress set to 0x%X", sub_addr);
+ return true;
+ };
+
+ bool send_byte(uint8_t data) {
+ LOG_F(9, "SDRAM byte 0x%X received", data);
+ return true;
+ };
+
+ bool receive_byte(uint8_t *p_data) {
+ if (this->pos >= this->eeprom_data[0]) {
+ this->pos = 0; /* attempt to read past SPD data should wrap around */
+ }
+ LOG_F(9, "SDRAM sending EEPROM byte 0x%X", this->eeprom_data[this->pos]);
+ *p_data = this->eeprom_data[this->pos++];
+ return true;
+ };
+
+private:
+ uint8_t dev_addr; /* I2C address */
+ int pos; /* actual read position */
+
+ /* EEPROM content */
+ uint8_t eeprom_data[256] = {
+ 128, /* number of bytes present */
+ 8, /* log2(EEPROM size) */
+ RAMType::SDRAM, /* memory type */
+
+ /* the following fields will be set up in set_capacity() */
+ 0, /* number of row addresses */
+ 0, /* number of column addresses */
+ 0 /* number of banks */
+ };
+};
+
+#endif /* SPD_EEPROM_H */
diff --git a/machines/machinegossamer.cpp b/machines/machinegossamer.cpp
index b6a5cdf..e8864e6 100644
--- a/machines/machinegossamer.cpp
+++ b/machines/machinegossamer.cpp
@@ -31,6 +31,22 @@ along with this program. If not, see .
#include "devices/machineid.h"
#include "devices/macio.h"
#include "devices/viacuda.h"
+#include "devices/spdram.h"
+
+static void setup_ram_slot(std::string name, int i2c_addr, int capacity_megs)
+{
+ if (!capacity_megs)
+ return;
+
+ gMachineObj->add_component(name, new SpdSdram168(i2c_addr));
+ SpdSdram168 *ram_dimm = dynamic_cast(gMachineObj->get_comp_by_name(name));
+ ram_dimm->set_capacity(capacity_megs);
+
+ /* register RAM DIMM with the I2C bus */
+ I2CBus *i2c_bus = dynamic_cast(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST));
+ i2c_bus->register_device(i2c_addr, ram_dimm);
+}
+
int create_gossamer()
{
@@ -67,6 +83,11 @@ int create_gossamer()
return -1;
}
+ /* configure RAM slots */
+ setup_ram_slot("RAM_DIMM_1", 0x57, 64); /* RAM slot 1 -> 64MB by default */
+ setup_ram_slot("RAM_DIMM_2", 0x56, 0); /* RAM slot 2 -> empty by default */
+ setup_ram_slot("RAM_DIMM_3", 0x55, 0); /* RAM slot 3 -> empty by default */
+
/* Init virtual CPU and request MPC750 CPU aka G3 */
ppc_cpu_init(grackle_obj, PPC_VER::MPC750);