diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt
index 36a654d..c75a73a 100644
--- a/devices/CMakeLists.txt
+++ b/devices/CMakeLists.txt
@@ -6,6 +6,7 @@ file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/common/adb/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/common/i2c/*.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/common/nubus/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp"
diff --git a/devices/common/hwcomponent.h b/devices/common/hwcomponent.h
index 6543b1b..50c4d15 100644
--- a/devices/common/hwcomponent.h
+++ b/devices/common/hwcomponent.h
@@ -39,6 +39,8 @@ enum HWCompType {
I2C_DEV = 1ULL << 9, /* I2C device */
ADB_HOST = 1ULL << 12, /* ADB host */
ADB_DEV = 1ULL << 13, /* ADB device */
+ PDS_DEV = 1ULL << 14, /* processor direct slot (PDS) device */
+ NUBUS_DEV = 1ULL << 15, /* Nubus device */
INT_CTRL = 1ULL << 16, /* interrupt controller */
SCSI_BUS = 1ULL << 20, /* SCSI bus */
SCSI_HOST = 1ULL << 21, /* SCSI host adapter */
diff --git a/devices/common/nubus/nubusutils.cpp b/devices/common/nubus/nubusutils.cpp
new file mode 100644
index 0000000..eca08a7
--- /dev/null
+++ b/devices/common/nubus/nubusutils.cpp
@@ -0,0 +1,146 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-22 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 .
+*/
+
+#include
+#include
+#include
+#include
+#include "memaccess.h"
+
+#include
+#include
+
+uint32_t calculate_rom_crc(uint8_t *data_ptr, int length)
+{
+ uint32_t sum = 0;
+
+ for (int i = 0; i < length; i++) {
+ // rotate sum left by one bit
+ if (sum & 0x80000000UL) {
+ sum = (sum << 1) | 1;
+ } else {
+ sum = (sum << 1) | 0;
+ }
+ sum += data_ptr[i];
+ }
+
+ return sum;
+}
+
+int load_declaration_rom(const std::string rom_path, int slot_num)
+{
+ std::ifstream rom_file;
+
+ int result = 0;
+
+ try {
+ rom_file.open(rom_path, std::ios::in | std::ios::binary);
+ if (rom_file.fail()) {
+ throw std::runtime_error("could not open declaration ROM image");
+ }
+
+ // determine image size
+ rom_file.seekg(0, std::ios::end);
+ uint32_t rom_size = rom_file.tellg();
+
+ // load it
+ auto rom_data = std::unique_ptr (new uint8_t[rom_size]);
+ rom_file.seekg(0, std::ios::beg);
+ rom_file.read((char *)rom_data.get(), rom_size);
+
+ // verify image data
+ uint8_t byte_lane = rom_data[rom_size - 1];
+ if ((byte_lane & 0xF) != ((byte_lane >> 4) ^ 0xF)) {
+ throw std::runtime_error("invalid byte lane value");
+ }
+
+ if (READ_DWORD_BE_A(&rom_data[rom_size - 6]) != 0x5A932BC7UL) {
+ throw std::runtime_error("invalid test pattern");
+ }
+
+ if (rom_data[rom_size - 7] != 1) {
+ throw std::runtime_error("unsupported format");
+ }
+
+ uint32_t crc = READ_DWORD_BE_A(&rom_data[rom_size - 12]);
+ int hdr_length = READ_DWORD_BE_A(&rom_data[rom_size - 16]);
+
+ // patch the CRC field of the format header to 0
+ WRITE_DWORD_BE_A(&rom_data[rom_size - 12], 0);
+
+ uint32_t test_crc = calculate_rom_crc(rom_data.get(), hdr_length);
+
+ // restore the CRC field value
+ WRITE_DWORD_BE_A(&rom_data[rom_size - 12], crc);
+
+ if (test_crc != crc) {
+ throw std::runtime_error("invalid CRC");
+ }
+
+ int lanes_used = 0;
+
+ // count meaningful lanes in a data quad
+ for (int i = 0; i < 4; i++) {
+ if (byte_lane & (1 << i)) {
+ lanes_used++;
+ }
+ }
+
+ // calculate padded size = rom_size * (4 / lanes_used)
+ int padded_len = static_cast((float)rom_size * (4 / (float)lanes_used) + 0.5f);
+
+ // calculate starting physical address of the ROM
+ uint32_t rom_phys_start = (0xF0FFFFFFUL | ((slot_num & 0xF) << 24)) - padded_len + 1;
+
+ auto mem_crtl = dynamic_cast(gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL));
+ if (!mem_crtl->add_rom_region(rom_phys_start, padded_len)) {
+ throw std::runtime_error("could not allocate ROM space");
+ }
+
+ int data_pos = 0;
+
+ auto new_data = std::unique_ptr (new uint8_t[padded_len]);
+
+ // prepare new ROM data by copying over used lanes
+ // and padding unused lanes with 0xFF
+ // NOTE: speed uncritical - don't optimize!
+ for (int i = 0; i < padded_len; i += 4) {
+ for (int b = 0; b < 4; b++) {
+ if (byte_lane & (1 << b)) {
+ new_data[i+b] = rom_data[data_pos++];
+ } else {
+ new_data[i+b] = 0xFF;
+ }
+ }
+ }
+
+ // move padded ROM data to the memory region
+ mem_crtl->set_data(rom_phys_start, new_data.get(), padded_len);
+ }
+ catch (const std::exception& exc) {
+ LOG_F(ERROR, "load_declaration_rom failed: %s", exc.what());
+ result = -1;
+ }
+
+ rom_file.close();
+
+ return result;
+}
diff --git a/devices/common/nubus/nubusutils.h b/devices/common/nubus/nubusutils.h
new file mode 100644
index 0000000..bcee4a5
--- /dev/null
+++ b/devices/common/nubus/nubusutils.h
@@ -0,0 +1,30 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-22 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 .
+*/
+
+#ifndef NUBUS_UTILS_H
+#define NUBUS_UTILS_H
+
+#include
+#include
+
+int load_declaration_rom(const std::string rom_path, int slot_num);
+
+#endif // NUBUS_UTILS_H