From 822f6cafd2bd84dabc48352de386937f9d3b54ba Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Fri, 13 Mar 2020 22:49:58 +0100 Subject: [PATCH] Create machine factory. It manages various hardware configurations referred to as machines. --- CMakeLists.txt | 8 +- cpu/ppc/ppcemu.h | 4 +- cpu/ppc/ppcexec.cpp | 4 +- machines/CMakeLists.txt | 7 ++ machines/machinebase.cpp | 76 ++++++++++++++++++ machines/machinebase.h | 68 ++++++++++++++++ machines/machinefactory.cpp | 151 +++++++++++++++++++++++++++++++++++ machines/machinefactory.h | 36 +++++++++ machines/machinegossamer.cpp | 77 ++++++++++++++++++ main.cpp | 144 ++------------------------------- thirdparty/CMakeLists.txt | 2 + 11 files changed, 435 insertions(+), 142 deletions(-) create mode 100644 machines/CMakeLists.txt create mode 100644 machines/machinebase.cpp create mode 100644 machines/machinebase.h create mode 100644 machines/machinefactory.cpp create mode 100644 machines/machinefactory.h create mode 100644 machines/machinegossamer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 508ccff..686e9ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) add_subdirectory("${PROJECT_SOURCE_DIR}/cpu/ppc/") add_subdirectory("${PROJECT_SOURCE_DIR}/devices/") +add_subdirectory("${PROJECT_SOURCE_DIR}/machines/") add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/") include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/devices" @@ -21,10 +22,13 @@ file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.cpp" file(GLOB TEST_SOURCES "${PROJECT_SOURCE_DIR}/cpu/ppc/test/*.cpp") -add_executable(dingusppc ${SOURCES} $ $ +add_executable(dingusppc ${SOURCES} $ + $ + $ $) -add_executable(testppc ${TEST_SOURCES} $ $ +add_executable(testppc ${TEST_SOURCES} $ + $ $) add_custom_command( diff --git a/cpu/ppc/ppcemu.h b/cpu/ppc/ppcemu.h index 58af04c..163491b 100644 --- a/cpu/ppc/ppcemu.h +++ b/cpu/ppc/ppcemu.h @@ -245,8 +245,8 @@ extern uint32_t exceptions_performed; extern uint32_t supervisor_inst_num; //Function prototypes -extern void ppc_cpu_init(uint32_t proc_version); -extern void ppc_mmu_init(void); +extern void ppc_cpu_init(MemCtrlBase *mem_ctrl, uint32_t proc_version); +extern void ppc_mmu_init(); void ppc_illegalop(); void ppc_opcode4(); diff --git a/cpu/ppc/ppcexec.cpp b/cpu/ppc/ppcexec.cpp index 3179f88..a123cc5 100644 --- a/cpu/ppc/ppcexec.cpp +++ b/cpu/ppc/ppcexec.cpp @@ -760,10 +760,12 @@ again: } #endif -void ppc_cpu_init(uint32_t proc_version) +void ppc_cpu_init(MemCtrlBase *mem_ctrl, uint32_t proc_version) { int i; + mem_ctrl_instance = mem_ctrl; + clock_test_begin = clock(); timebase_counter = 0; diff --git a/machines/CMakeLists.txt b/machines/CMakeLists.txt new file mode 100644 index 0000000..fd9bd5c --- /dev/null +++ b/machines/CMakeLists.txt @@ -0,0 +1,7 @@ +set(CMAKE_CXX_STANDARD 11) + +include_directories("${PROJECT_SOURCE_DIR}") + +file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") + +add_library(machines OBJECT ${SOURCES}) diff --git a/machines/machinebase.cpp b/machines/machinebase.cpp new file mode 100644 index 0000000..76f12b6 --- /dev/null +++ b/machines/machinebase.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include "machinebase.h" + +std::unique_ptr gMachineObj = 0; + + +MachineBase::MachineBase(std::string name) +{ + this->name = name; + + /* initialize internal maps */ + this->comp_map.clear(); + this->name_to_type.clear(); + this->aliases.clear(); +} + +MachineBase::~MachineBase() +{ + for(auto it = this->comp_map.begin(); it != this->comp_map.end(); it++) { + delete it->second; + } + this->comp_map.clear(); +} + +bool MachineBase::add_component(std::string name, HWCompType type, MMIODevice *dev_obj) +{ + if (this->comp_map.count(name)) { + LOG_F(ERROR, "Component %s already exists!", name.c_str()); + return false; + } + + this->comp_map[name] = dev_obj; + this->name_to_type[name] = type; + + return true; +} + +void MachineBase::add_alias(std::string name, std::string alias, HWCompType type) +{ + this->aliases[alias] = name; + this->name_to_type[alias] = type; +} + +MMIODevice *MachineBase::get_comp_by_name(std::string name) +{ + if (this->aliases.count(name)) { + name = this->aliases[name]; + } + + if (this->comp_map.count(name)) { + return this->comp_map[name]; + } else { + return NULL; + } +} + +MMIODevice *MachineBase::get_comp_by_type(HWCompType type) +{ + std::string comp_name; + bool found = false; + + for(auto it = this->name_to_type.begin(); it != this->name_to_type.end(); it++) { + if (it->second == type) { + comp_name = it->first; + found = true; + break; + } + } + + if (!found) + return NULL; + + return this->get_comp_by_name(comp_name); +} diff --git a/machines/machinebase.h b/machines/machinebase.h new file mode 100644 index 0000000..b3a1928 --- /dev/null +++ b/machines/machinebase.h @@ -0,0 +1,68 @@ +/* +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 Base class for managing different HW components of a machine. + + Author: Max Poliakovski + */ + +#ifndef MACHINE_BASE_H +#define MACHINE_BASE_H + +#include +#include +#include +#include "devices/mmiodevice.h" + +/** types of different HW components */ +enum HWCompType : int { + UNKNOWN = 0, /* unknown component type */ + MEM_CTRL = 1, /* memory controller */ + ROM = 2, /* read-only memory */ + RAM = 3, /* random access memory */ + MMIO_DEV = 4, /* memory mapped I/O device */ + PCI_HOST = 5, /* PCI host */ + PCI_DEV = 6, /* PCI device */ + I2C_DEV = 7, /* I2C device */ + ADB_DEV = 8 /* ADB device */ +}; + +class MachineBase +{ +public: + MachineBase(std::string name); + ~MachineBase(); + + bool add_component(std::string name, HWCompType type, MMIODevice *dev_obj); + void add_alias(std::string name, std::string alias, HWCompType type); + MMIODevice *get_comp_by_name(std::string name); + MMIODevice *get_comp_by_type(HWCompType type); + +private: + std::string name; + std::mapcomp_map; + std::map name_to_type; + std::map aliases; +}; + +extern std::unique_ptr gMachineObj; + +#endif /* MACHINE_BASE_H */ diff --git a/machines/machinefactory.cpp b/machines/machinefactory.cpp new file mode 100644 index 0000000..521ebe2 --- /dev/null +++ b/machines/machinefactory.cpp @@ -0,0 +1,151 @@ +/* +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 Factory for creating different machines. + + Author: Max Poliakovski + */ + +#include +#include +#include +#include +#include +#include +#include "memreadwrite.h" +#include "machinefactory.h" +#include "devices/memctrlbase.h" + +using namespace std; + +/** + Power Macintosh ROM identification string + + is located in the ConfigInfo structure starting at 0x30D064 (PCI Macs) + or 0x30C064 (Nubus Macs). +*/ +static const map rom_identity = { + {0x416C6368, "Performa 6400"}, //Alchemy + //{"Come", "PowerBook 2400"}, //Comet + {0x436F7264, "Power Mac 5200/6200 series"}, //Cordyceps + {0x47617A65, "Power Mac 6500"}, //Gazelle + {0x476F7373, "Power Mac G3 Beige"}, //Gossamer + {0x47525820, "PowerBook G3 Wallstreet"}, + //{"Hoop", "PowerBook 3400"}, //Hooper + {0x50425820, "PowerBook Pre-G3"}, + {0x50444D20, "Nubus Power Mac or WGS"}, //Piltdown Man (6100/7100/8100) + {0x50697020, "Bandai Pippin"}, //Pippin + //{"Powe", "Generic Power Mac"}, //PowerMac? + //{"Spar", "20th Anniversay Mac"}, //Spartacus + //{"Tanz", "Power Mac 4400"}, //Tanzania + {0x544E5420, "Power Mac 7xxxx/8xxx series"}, //Trinitrotoluene :-) + //{"Zanz", "A complete engima."}, //Zanzibar (mentioned in Sheepshaver's code, but no match to any known ROM) + //{"????", "A clone, perhaps?"} //N/A (Placeholder ID) +}; + + +int create_machine_for_id(uint32_t id) +{ + switch(id) { + case 0x476F7373: + create_gossamer(); + break; + default: + LOG_F(ERROR, "Unknown machine ID: %X", id); + return -1; + } + return 0; +} + + +/* Read ROM file content and transfer it to the dedicated ROM region */ +void load_rom(ifstream& rom_file, uint32_t file_size) +{ + unsigned char *sysrom_mem = new unsigned char[file_size]; + + rom_file.seekg(0, ios::beg); + rom_file.read((char *)sysrom_mem, file_size); + + MemCtrlBase *mem_ctrl = dynamic_cast + (gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL)); + + mem_ctrl->set_data(0xFFC00000, sysrom_mem, file_size); + delete[] sysrom_mem; +} + + +int create_machine_for_rom(const char* rom_filepath) +{ + ifstream rom_file; + int result; + uint32_t file_size, config_info_offset, rom_id; + char rom_id_str[17]; + + rom_file.open(rom_filepath, ios::in|ios::binary); + if (rom_file.fail()) { + LOG_F(ERROR, "Cound not open the specified ROM file."); + rom_file.close(); + return -1; + } + + rom_file.seekg(0, rom_file.end); + file_size = rom_file.tellg(); + rom_file.seekg(0, rom_file.beg); + + if (file_size != 0x400000UL){ + LOG_F(ERROR, "Unxpected ROM File size. Expected size is 4 megabytes."); + rom_file.close(); + return -1; + } + + /* read config info offset from file */ + config_info_offset = 0; + rom_file.seekg(0x300080, ios::beg); + rom_file.read((char *)&config_info_offset, 4); + config_info_offset = READ_DWORD_BE_A(&config_info_offset); + + /* rewind to ConfigInfo.BootstrapVersion field */ + rom_file.seekg(0x300064 + config_info_offset, ios::beg); + + /* read BootstrapVersion as C string */ + rom_file.read(rom_id_str, 16); + rom_id_str[16] = 0; + LOG_F(INFO, "ROM BootstrapVersion: %s", rom_id_str); + + if (strncmp(rom_id_str, "Boot", 4) != 0) { + LOG_F(ERROR, "Invalid BootstrapVersion string."); + rom_file.close(); + return -1; + } + + /* convert BootstrapVersion string to ROM ID */ + rom_id = (rom_id_str[5] << 24) | (rom_id_str[6] << 16) | + (rom_id_str[7] << 8) | rom_id_str[8]; + + LOG_F(INFO, "The machine is identified as... %s\n", rom_identity.at(rom_id).c_str()); + + create_machine_for_id(rom_id); + + load_rom(rom_file, file_size); + + rom_file.close(); + return 0; +} diff --git a/machines/machinefactory.h b/machines/machinefactory.h new file mode 100644 index 0000000..eb349c1 --- /dev/null +++ b/machines/machinefactory.h @@ -0,0 +1,36 @@ +/* +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 Factory for creating different machines. + + Author: Max Poliakovski + */ + +#ifndef MACHINE_FACTORY_H +#define MACHINE_FACTORY_H + +#include "machinebase.h" + +int create_machine_for_rom(const char* rom_filepath); + +int create_gossamer(void); + +#endif /* MACHINE_FACTORY_H */ diff --git a/machines/machinegossamer.cpp b/machines/machinegossamer.cpp new file mode 100644 index 0000000..0cdd11e --- /dev/null +++ b/machines/machinegossamer.cpp @@ -0,0 +1,77 @@ +/* +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 Constructs the Gossamer machine. + + Author: Max Poliakovski + */ + +#include +#include "machinebase.h" +#include "devices/mpc106.h" +#include "devices/machineid.h" +#include "devices/macio.h" +#include "cpu/ppc/ppcemu.h" +#include "machinebase.h" + +int create_gossamer() +{ + if (gMachineObj) { + LOG_F(ERROR, "Global machine object not empty!"); + return -1; + } + + LOG_F(INFO, "Initializing the Gossamer hardware..."); + + /* initialize the global machine object */ + gMachineObj.reset(new MachineBase("Gossamer")); + + /* register MPC106 aka Grackle as memory controller and PCI host */ + gMachineObj->add_component("Grackle", HWCompType::MEM_CTRL, new MPC106); + gMachineObj->add_alias("Grackle", "PCI_Host", HWCompType::PCI_HOST); + + /* get raw pointer to MPC106 object */ + MPC106 *grackle_obj = dynamic_cast(gMachineObj->get_comp_by_name("Grackle")); + + /* add the machine ID register */ + gMachineObj->add_component("MachineID", HWCompType::MMIO_DEV, + new GossamerID(0x3d8c)); + grackle_obj->add_mmio_region(0xFF000004, 4096, + gMachineObj->get_comp_by_name("MachineID")); + + /* add the Heathrow I/O controller */ + gMachineObj->add_component("Heathrow", HWCompType::MMIO_DEV, new HeathrowIC); + grackle_obj->pci_register_device(16, + dynamic_cast(gMachineObj->get_comp_by_name("Heathrow"))); + + /* allocate ROM region */ + if (!grackle_obj->add_rom_region(0xFFC00000, 0x400000)) { + LOG_F(ERROR, "Could not allocate ROM region!\n"); + return -1; + } + + /* Init virtual CPU and request MPC750 CPU aka G3 */ + ppc_cpu_init(grackle_obj, PPC_VER::MPC750); + + LOG_F(INFO, "Initialization complete.\n"); + + return 0; +} diff --git a/main.cpp b/main.cpp index 2593bbf..6bb1a07 100644 --- a/main.cpp +++ b/main.cpp @@ -24,54 +24,15 @@ along with this program. If not, see . #include #include -#include #include #include -#include #include -#include -#include #include "ppcemu.h" -#include "ppcmmu.h" -#include "memreadwrite.h" -#include "devices/mpc106.h" #include "debugger/debugger.h" -#include "devices/machineid.h" -#include "devices/macio.h" -#include "devices/mpc106.h" +#include "machines/machinefactory.h" using namespace std; -/** - Power Macintosh ROM identification string - - is located in the ConfigInfo structure starting at 0x30D064 (PCI Macs) - or 0x30C064 (Nubus Macs). This helps a lot to determine which - hardware is to be used. -*/ -static const map PPCMac_ROMIdentity = { //Codename Abbreviation for... - {"Alch", "Performa 6400"}, //Alchemy - {"Come", "PowerBook 2400"}, //Comet - {"Cord", "Power Mac 5200/6200 series"}, //Cordyceps - {"Gaze", "Power Mac 6500"}, //Gazelle - {"Goss", "Power Mac G3 Beige"}, //Gossamer - {"GRX ", "PowerBook G3 Wallstreet"}, //(Unknown) - {"Hoop", "PowerBook 3400"}, //Hooper - {"PBX ", "PowerBook Pre-G3"}, //(Unknown) - {"PDM ", "Nubus Power Mac or WGS"}, //Piltdown Man (6100/7100/8100) - {"Pip ", "Pippin... uh... yeah..."}, //Pippin - {"Powe", "Generic Power Mac"}, //PowerMac? - {"Spar", "20th Anniversay Mac, you lucky thing."}, //Spartacus - {"Tanz", "Power Mac 4400"}, //Tanzania - {"TNT ", "Power Mac 7xxxx/8xxx series"}, //Trinitrotoluene :-) - {"Zanz", "A complete engima."}, //Zanzibar (mentioned in Sheepshaver's code, but no match to any known ROM) - {"????", "A clone, perhaps?"} //N/A (Placeholder ID) -}; - -HeathrowIC *heathrow = 0; -GossamerID *machine_id; - - int main(int argc, char **argv) { @@ -106,101 +67,10 @@ int main(int argc, char **argv) uint32_t rom_filesize; - /* Init virtual CPU and request MPC750 CPU aka G3 */ - ppc_cpu_init(PPC_VER::MPC750); - - - LOG_F(INFO, "Checking for ROM file"); - - //Open the ROM File. - ifstream romFile; - - romFile.open("rom.bin", ios::in|ios::binary); - - if (romFile.fail()){ - cerr << "rom.bin not present. Please provide an appropriate ROM file" - << " and restart this program.\n"; - - romFile.close(); - return 1; + if (create_machine_for_rom("rom.bin")) { + goto bail; } - //Calculate and validate ROM file size. - romFile.seekg(0, romFile.end); - rom_filesize = (uint32_t) romFile.tellg(); - LOG_F(INFO, "Rom SIZE: %d \n", rom_filesize); - romFile.seekg (0, romFile.beg); - - if (rom_filesize != 0x400000){ - cerr << "Unsupported ROM File size. Expected size is 4 megabytes.\n"; - romFile.close(); - return 1; - } - - char configGrab = 0; - uint32_t configInfoOffset = 0; - - romFile.seekg (0x300082, ios::beg); //This is where the place to get the offset is - romFile.get(configGrab); //just one byte to determine ConfigInfo location - configInfoOffset = (uint32_t)(configGrab & 0xff); - - uint32_t configInfoAddr = 0x300000 + (configInfoOffset << 8) + 0x69; //address to check the identifier string - char memPPCBlock[5] = { 0 }; //First four chars are enough to distinguish between codenames - romFile.seekg (configInfoAddr, ios::beg); - romFile.read(memPPCBlock, 4); - memPPCBlock[4] = 0; - uint32_t rom_id = READ_DWORD_BE_A(memPPCBlock); - - std::string string_test = std::string(memPPCBlock); - - //Just auto-iterate through the list - for (auto iter = PPCMac_ROMIdentity.begin(); iter != PPCMac_ROMIdentity.end(); ){ - - string redo_me = iter->first; - - if (string_test.compare(redo_me) == 0){ - const char* check_me = iter->second.c_str(); - LOG_F(INFO, "The machine is identified as... %s \n", check_me); - romFile.seekg (0x0, ios::beg); - break; - } - else{ - iter++; - } - } - - switch(rom_id) { - case 0x476F7373: { - LOG_F(INFO, "Initialize Gossamer hardware... \n"); - MPC106 *mpc106 = new MPC106(); - mem_ctrl_instance = mpc106; - if (!mem_ctrl_instance->add_rom_region(0xFFC00000, 0x400000)) { - LOG_F(ERROR, "Failed to Gossamer hardware... \n"); - delete(mem_ctrl_instance); - romFile.close(); - return 1; - } - machine_id = new GossamerID(0x3d8c); - mpc106->add_mmio_region(0xFF000004, 4096, machine_id); - - heathrow = new HeathrowIC(); - mpc106->pci_register_device(16, heathrow); - LOG_F(INFO, "Initialization complete. \n"); - } - break; - default: - LOG_F(INFO, "This machine not supported yet. \n"); - return 1; - } - - /* Read ROM file content and transfer it to the dedicated ROM region */ - unsigned char *sysrom_mem = new unsigned char[rom_filesize]; - romFile.read ((char *)sysrom_mem, rom_filesize); - mem_ctrl_instance->set_data(0xFFC00000, sysrom_mem, rom_filesize); - romFile.close(); - delete[] sysrom_mem; - - if ((checker == "1") || (checker == "realtime") || \ (checker == "-realtime") || (checker == "/realtime")) { ppc_exec(); @@ -219,10 +89,10 @@ int main(int argc, char **argv) std::cout << "debugger - Enter the interactive debugger. " << endl; } - /* Free memory after the emulation is completed. */ - delete(heathrow); - delete(machine_id); - delete(mem_ctrl_instance); +bail: + LOG_F(INFO, "Cleaning up..."); + + delete gMachineObj.release(); return 0; } diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index f37b50e..b333ae1 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,3 +1,5 @@ +set(CMAKE_CXX_STANDARD 11) + include_directories("${PROJECT_SOURCE_DIR}") file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/loguru.cpp"