dingusppc/machines/machinefactory.cpp
joevt 64fec88436 Fix compiler warnings: cast loses precision.
Use explicit cast when converting large integer types to smaller integer types when it is known that the most significant bytes are not required.
For pcidevice, check the ROM file size before casting to int. We'll allow expansion ROM sizes up to 4MB but usually they are 64K, sometimes 128K, rarely 256K.
for machinefactory, change the type to size_t so that it can correctly get the size of files that are larger than 4GB; it already checks the file size is 4MB before we need to cast to uint32_t.
For floppyimg, check the image size before casting to int. For raw images, only allow files up to 2MB. For DiskCopy42 images, it already checks the file size, so do the cast after that.
2023-01-11 01:17:12 -08:00

380 lines
12 KiB
C++

/*
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 <https://www.gnu.org/licenses/>.
*/
/** @file Factory for creating different machines.
Author: Max Poliakovski
*/
#include <devices/deviceregistry.h>
#include <devices/memctrl/memctrlbase.h>
#include <devices/sound/soundserver.h>
#include <loguru.hpp>
#include <machines/machinebase.h>
#include <machines/machinefactory.h>
#include <machines/machineproperties.h>
#include <memaccess.h>
#include <cinttypes>
#include <cstring>
#include <fstream>
#include <functional>
#include <iostream>
#include <iomanip>
#include <map>
#include <memory>
#include <string>
#include <vector>
using namespace std;
map<string, unique_ptr<BasicProperty>> gMachineSettings;
/**
Power Macintosh ROM identification map.
Maps Bootstrap string located at offset 0x30D064 (PCI Macs)
or 0x30C064 (Nubus Macs) to machine name and description.
*/
static const map<uint32_t, std::tuple<string, const char*>> rom_identity = {
{0x416C6368, {"pm6400", "Performa 6400"}}, // Alchemy
//{"Come", "PowerBook 2400"}, // Comet
{0x436F7264, {"pm5200", "Power Mac 5200/6200 series"}}, // Cordyceps
{0x47617A65, {"pm6500", "Power Mac 6500"}}, // Gazelle
{0x476F7373, {"pmg3dt", "Power Mac G3 Beige"}}, // Gossamer
{0x47525820, {"pbg3", "PowerBook G3 Wallstreet"}},
//{"Hoop", "PowerBook 3400"}, // Hooper
{0x50425820, {"pb-preg3", "PowerBook Pre-G3"}},
{0x50444D20, {"pm6100", "Nubus Power Mac"}}, // Piltdown Man (6100/7100/8100)
{0x50697020, {"pippin", "Bandai Pippin"}}, // Pippin
//{"Powe", "Generic Power Mac"}, // PowerMac?
{0x544E5420, {"pm7200", "Power Mac 7xxxx/8xxx series"}}, // Trinitrotoluene :-)
{0x5A616E7A, {"pm4400", "Power Mac 4400/7220"}}, // Zanzibar
};
static const map<string, string> PropHelp = {
{"rambank1_size", "specifies RAM bank 1 size in MB"},
{"rambank2_size", "specifies RAM bank 2 size in MB"},
{"rambank3_size", "specifies RAM bank 3 size in MB"},
{"rambank4_size", "specifies RAM bank 4 size in MB"},
{"gfxmem_size", "specifies video memory size in MB"},
{"fdd_img", "specifies path to floppy disk image"},
{"fdd_fmt", "specifies floppy disk format"},
{"fdd_wr_prot", "toggles floppy disk's write protection"},
{"mon_id", "specifies which monitor to emulate"},
{"pci_A1", "insert a PCI device into A1 slot"},
{"pci_B1", "insert a PCI device into B1 slot"},
{"pci_C1", "insert a PCI device into C1 slot"},
{"serial_backend", "specifies the backend for the serial port"},
{"emmo", "enables/disables factory HW tests during startup"},
};
bool MachineFactory::add(const string& machine_id, MachineDescription desc)
{
if (get_registry().find(machine_id) != get_registry().end()) {
return false;
}
get_registry()[machine_id] = desc;
return true;
}
void MachineFactory::list_machines()
{
cout << endl << "Supported machines:" << endl << endl;
for (auto& m : get_registry()) {
cout << setw(13) << m.first << "\t\t" << m.second.description << endl;
}
cout << endl;
}
void MachineFactory::create_device(string& dev_name, DeviceDescription& dev)
{
for (auto& subdev_name : dev.subdev_list) {
create_device(subdev_name, DeviceRegistry::get_descriptor(subdev_name));
}
gMachineObj->add_device(dev_name, dev.m_create_func());
}
int MachineFactory::create(string& mach_id)
{
auto it = get_registry().find(mach_id);
if (it == get_registry().end()) {
LOG_F(ERROR, "Unknown machine id %s", mach_id.c_str());
return -1;
}
LOG_F(INFO, "Initializing %s hardware...", it->second.description.c_str());
// initialize global machine object
gMachineObj.reset(new MachineBase(it->second.name));
// create and register sound server
gMachineObj->add_device("SoundServer", std::unique_ptr<SoundServer>(new SoundServer()));
// recursively create device objects
for (auto& dev_name : it->second.devices) {
create_device(dev_name, DeviceRegistry::get_descriptor(dev_name));
}
if (it->second.init_func(mach_id)) {
LOG_F(ERROR, "Machine initialization function failed!");
return -1;
}
// post-initialize all devices
if (gMachineObj->postinit_devices()) {
LOG_F(ERROR, "Could not post-initialize devices!");
return -1;
}
LOG_F(INFO, "Initialization completed.");
return 0;
}
void MachineFactory::list_properties()
{
cout << endl;
for (auto& mach : get_registry()) {
cout << mach.second.description << " supported properties:" << endl << endl;
print_settings(mach.second.settings);
for (auto& d : mach.second.devices) {
list_device_settings(DeviceRegistry::get_descriptor(d));
}
}
cout << endl;
}
void MachineFactory::list_device_settings(DeviceDescription& dev)
{
for (auto& d : dev.subdev_list) {
list_device_settings(DeviceRegistry::get_descriptor(d));
}
print_settings(dev.properties);
}
void MachineFactory::print_settings(PropMap& prop_map)
{
string help;
for (auto& p : prop_map) {
if (PropHelp.find(p.first) != PropHelp.end())
help = PropHelp.at(p.first);
else
help = "";
cout << setw(13) << p.first << "\t\t" << help << endl;
cout << setw(13) << "\t\t\t" "Valid values: ";
switch(p.second->get_type()) {
case PROP_TYPE_INTEGER:
cout << dynamic_cast<IntProperty*>(p.second)->get_valid_values_as_str()
<< endl;
break;
case PROP_TYPE_STRING:
cout << dynamic_cast<StrProperty*>(p.second)->get_valid_values_as_str()
<< endl;
break;
case PROP_TYPE_BINARY:
cout << dynamic_cast<BinProperty*>(p.second)->get_valid_values_as_str()
<< endl;
break;
default:
break;
}
cout << endl;
}
}
void MachineFactory::get_device_settings(DeviceDescription& dev, map<string, string> &settings)
{
for (auto& d : dev.subdev_list) {
get_device_settings(DeviceRegistry::get_descriptor(d), settings);
}
for (auto& p : dev.properties) {
settings[p.first] = p.second->get_string();
// populate dynamic machine settings from presets
gMachineSettings[p.first] = unique_ptr<BasicProperty>(p.second->clone());
}
}
int MachineFactory::get_machine_settings(const string& id, map<string, string> &settings)
{
auto it = get_registry().find(id);
if (it != get_registry().end()) {
auto props = it->second.settings;
gMachineSettings.clear();
for (auto& p : props) {
settings[p.first] = p.second->get_string();
// populate dynamic machine settings from presets
gMachineSettings[p.first] = unique_ptr<BasicProperty>(p.second->clone());
}
for (auto& dev : it->second.devices) {
get_device_settings(DeviceRegistry::get_descriptor(dev), settings);
}
} else {
LOG_F(ERROR, "Unknown machine id %s", id.c_str());
return -1;
}
return 0;
}
void MachineFactory::set_machine_settings(map<string, string> &settings) {
for (auto& s : settings) {
gMachineSettings.at(s.first)->set_string(s.second);
}
// print machine settings summary
cout << endl << "Machine settings summary: " << endl;
for (auto& p : gMachineSettings) {
cout << p.first << " : " << p.second->get_string() << endl;
}
}
string MachineFactory::machine_name_from_rom(string& rom_filepath) {
ifstream rom_file;
size_t file_size;
uint32_t config_info_offset, rom_id;
char rom_id_str[17];
string machine_name = "";
rom_file.open(rom_filepath, ios::in | ios::binary);
if (rom_file.fail()) {
LOG_F(ERROR, "Cound not open the specified ROM file.");
goto bail_out;
}
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.");
goto bail_out;
}
/* 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.");
goto bail_out;
}
/* 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",
std::get<1>(rom_identity.at(rom_id)));
machine_name = std::get<0>(rom_identity.at(rom_id));
bail_out:
rom_file.close();
return machine_name;
}
/* Read ROM file content and transfer it to the dedicated ROM region */
int MachineFactory::load_boot_rom(string& rom_filepath) {
ifstream rom_file;
size_t file_size;
int result;
AddressMapEntry *rom_reg;
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.");
result = -1;
} else {
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<MemCtrlBase*>(
gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL));
if ((rom_reg = mem_ctrl->find_rom_region())) {
mem_ctrl->set_data(rom_reg->start, sysrom_mem, (uint32_t)file_size);
} else {
ABORT_F("Could not locate physical ROM region!");
}
delete[] sysrom_mem;
result = 0;
}
rom_file.close();
return result;
}
int MachineFactory::create_machine_for_id(string& id, string& rom_filepath) {
if (MachineFactory::create(id) < 0 || load_boot_rom(rom_filepath) < 0) {
return -1;
}
return 0;
}