From f39188beb13f24d90945edc67d35450cb19cbc2f Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Sat, 4 Dec 2021 14:22:02 +0100 Subject: [PATCH] Initial support for floppy disk images. --- devices/CMakeLists.txt | 1 + devices/floppy/floppyimg.cpp | 208 +++++++++++++++++++++++++++++++++++ devices/floppy/floppyimg.h | 72 ++++++++++++ machines/machinefactory.cpp | 5 + machines/machinegossamer.cpp | 13 ++- machines/machinepdm.cpp | 7 ++ 6 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 devices/floppy/floppyimg.cpp create mode 100644 devices/floppy/floppyimg.h diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt index 85db403..36a654d 100644 --- a/devices/CMakeLists.txt +++ b/devices/CMakeLists.txt @@ -9,6 +9,7 @@ file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/floppy/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ioctrl/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/memctrl/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/serial/*.cpp" diff --git a/devices/floppy/floppyimg.cpp b/devices/floppy/floppyimg.cpp new file mode 100644 index 0000000..372f155 --- /dev/null +++ b/devices/floppy/floppyimg.cpp @@ -0,0 +1,208 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-21 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 Support for reading and writing of various floppy images. */ + +#include "floppyimg.h" +#include +#include + +#include +#include +#include +#include + +static FlopImgType identify_image(FILE *img_file) +{ + // WOZ images identification strings + static uint8_t WOZ1_SIG[] = {0x57, 0x4F, 0x5A, 0x31, 0xFF, 0x0A, 0x0D, 0x0A}; + static uint8_t WOZ2_SIG[] = {0x57, 0x4F, 0x5A, 0x32, 0xFF, 0x0A, 0x0D, 0x0A}; + + uint8_t buf[8] = { 0 }; + + fseek(img_file, 0, SEEK_SET); + fread(buf, sizeof(buf), 1, img_file); + + // WOZ files are easily identified + if (!std::memcmp(buf, WOZ1_SIG, sizeof(buf))) { + return FlopImgType::WOZ1; + } else if (!std::memcmp(buf, WOZ2_SIG, sizeof(buf))) { + return FlopImgType::WOZ2; + } else { + for (int offset = 0; offset <=84; offset += 84) { + // rewind to logical block 2 + fseek(img_file, 2*BLOCK_SIZE + offset, SEEK_SET); + fread(buf, sizeof(buf), 1, img_file); + + // check for HFS/MFS signature at the start of the logical block 2 + if ((buf[0] == 0x42 && buf[1] == 0x44) || + (buf[0] == 0xD2 && buf[1] == 0xD7)) { + if (offset) { + return FlopImgType::DC42; + } else { + return FlopImgType::RAW; + } + } + } + } + + return FlopImgType::UNKNOWN; +} + +static int64_t get_img_file_size(const char *fname) +{ + struct stat stat_buf; + + memset(&stat_buf, 0, sizeof(stat_buf)); + + int res = stat(fname, &stat_buf); + return res == 0 ? stat_buf.st_size : -1; +} + +static int64_t get_hfs_vol_size(const uint8_t *mdb_data) +{ + uint16_t drNmAlBlks = READ_WORD_BE_A(&mdb_data[18]); + uint32_t drAlBlkSiz = READ_DWORD_BE_A(&mdb_data[20]); + + // calculate size of the volume bitmap + uint32_t vol_bmp_size = (((drNmAlBlks + 8) >> 3) + 512) & 0xFFFFFE00UL; + + return (drNmAlBlks * drAlBlkSiz + vol_bmp_size + 3*BLOCK_SIZE); +} + +//======================= RAW IMAGE CONVERTER ============================ +RawFloppyImg::RawFloppyImg(const char *file_path) : FloppyImgConverter() +{ + this->img_path = file_path; +} + +/** For raw images, we're going to ensure that the data fits into + one of the supported floppy disk sizes as well as image size + matches the size of the embedded HFS/MFS volume. +*/ +int RawFloppyImg::validate() +{ + FILE *img_file = fopen(this->img_path, "rb"); + if (img_file == NULL) { + LOG_F(ERROR, "RawFloppyImg: Could not open specified floppy image!"); + return -1; + } + + // determine image size + this->img_size = get_img_file_size(this->img_path); + if (this->img_size < 5*BLOCK_SIZE) { + LOG_F(ERROR, "RawFloppyImg: image too short!"); + return -1; + } + + if (this->img_size > MFM_HD_SIZE) { + LOG_F(ERROR, "RawFloppyImg: image too big!"); + return -1; + } + + // read Master Directory Block from logical block 2 + uint8_t buf[512] = { 0 }; + + fseek(img_file, 2*BLOCK_SIZE, SEEK_SET); + fread(buf, sizeof(buf), 1, img_file); + fclose(img_file); + + uint64_t vol_size = 0; + + if (buf[0] == 0x42 && buf[1] == 0x44) { + // check HFS volume size + vol_size = get_hfs_vol_size(buf); + } else if (buf[0] == 0xD2 && buf[1] == 0xD7) { + // check MFS volume size + } else { + LOG_F(ERROR, "RawFloppyImg: unknown volume type!"); + return -1; + } + + if (vol_size > this->img_size) { + LOG_F(INFO, "RawFloppyImg: volume size > image size!"); + LOG_F(INFO, "Volume size: %llu, Image size: %llu", vol_size, this->img_size); + return -1; + } + + return 0; +} + +/** Guess physical parameters of the target virtual disk based on image size. */ +int RawFloppyImg::get_phys_params() +{ + // disk format: GCR vs MFM + // single-sided vs double-sided + // number of tracks + // number of sectors + return 0; +} + +/** Convert high-level image data to low-level disk data. */ +int RawFloppyImg::import_data() +{ + return 0; +} + +/** Convert low-level disk data to high-level image data. */ +int RawFloppyImg::export_data() +{ + return 0; +} + +int open_floppy_image(const char* img_path) +{ + FloppyImgConverter *fconv; + + FILE *img_file = fopen(img_path, "rb"); + if (img_file == NULL) { + LOG_F(ERROR, "Could not open specified floppy image!"); + return -1; + } + + FlopImgType itype = identify_image(img_file); + + fclose(img_file); + + switch(itype) { + case FlopImgType::RAW: + LOG_F(INFO, "Raw floppy image"); + fconv = new RawFloppyImg(img_path); + break; + case FlopImgType::DC42: + LOG_F(INFO, "Disk Copy 4.2 image"); + break; + case FlopImgType::WOZ1: + case FlopImgType::WOZ2: + LOG_F(INFO, "WOZ v%s image\n", (itype == FlopImgType::WOZ2) ? "2" : "1"); + break; + default: + LOG_F(ERROR, "Unknown/unsupported image format!"); + return -1; + } + + if (fconv->validate()) { + LOG_F(ERROR, "Image validation failed!"); + return -1; + } + + return 0; +} diff --git a/devices/floppy/floppyimg.h b/devices/floppy/floppyimg.h new file mode 100644 index 0000000..9534e9b --- /dev/null +++ b/devices/floppy/floppyimg.h @@ -0,0 +1,72 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-21 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 Definitions for working with various floppy images. */ + +#ifndef FLOPPY_IMG_H +#define FLOPPY_IMG_H + +#include + +#define BLOCK_SIZE 512 // size in bytes of a logical block + +#define MFM_HD_SIZE (BLOCK_SIZE*2880) // maximal size of a high-density floppy + +/** Floppy image types. */ +enum class FlopImgType { + RAW, // raw image without metadata or low-level formatting + DC42, // Disk Copy 4.2 image + WOZ1, // WOZ v1 image with metadata and low-level data retained + WOZ2, // WOZ v2 image with metadata and low-level data retained + UNKNOWN, // invalid/unknown image format +}; + +/** Interface for floppy image converters. */ +class FloppyImgConverter { +public: + FloppyImgConverter() = default; + ~FloppyImgConverter() = default; + + virtual int validate(void) = 0; + virtual int get_phys_params(void) = 0; + virtual int import_data(void) = 0; + virtual int export_data(void) = 0; +}; + +/** Converter for raw floppy images. */ +class RawFloppyImg : public FloppyImgConverter { +public: + RawFloppyImg(const char *file_path); + ~RawFloppyImg() = default; + + int validate(void); + int get_phys_params(void); + int import_data(void); + int export_data(void); + +private: + const char *img_path; + int64_t img_size; +}; + +extern int open_floppy_image(const char* img_path); + +#endif // FLOPPY_IMG_H diff --git a/machines/machinefactory.cpp b/machines/machinefactory.cpp index 8ac043d..7940c4e 100644 --- a/machines/machinefactory.cpp +++ b/machines/machinefactory.cpp @@ -76,6 +76,8 @@ static const PropMap GossamerSettings = { new IntProperty( 0, vector({0, 8, 16, 32, 64, 128, 256}))}, {"gfxmem_size", new IntProperty( 2, vector({2, 4, 6}))}, + {"fdd_img", + new StrProperty("")}, }; static const PropMap PDMSettings = { @@ -83,6 +85,8 @@ static const PropMap PDMSettings = { new IntProperty(0, vector({0, 8, 16, 32, 64, 128}))}, {"rambank2_size", new IntProperty(0, vector({0, 8, 16, 32, 64, 128}))}, + {"fdd_img", + new StrProperty("")}, }; static const map PropHelp = { @@ -90,6 +94,7 @@ static const map PropHelp = { {"rambank2_size", "specifies RAM bank 2 size in MB"}, {"rambank3_size", "specifies RAM bank 3 size in MB"}, {"gfxmem_size", "specifies video memory size in MB"}, + {"fdd_img", "specifies path to floppy disk image"}, }; static const map, string>> machines = { diff --git a/machines/machinegossamer.cpp b/machines/machinegossamer.cpp index 4711cc8..d3aab33 100644 --- a/machines/machinegossamer.cpp +++ b/machines/machinegossamer.cpp @@ -26,6 +26,7 @@ along with this program. If not, see . #include #include +#include #include #include #include @@ -89,9 +90,9 @@ int create_gossamer(std::string& id) { } /* configure RAM slots */ - setup_ram_slot("RAM_DIMM_1", 0x57, GET_INT_PROP("rambank1_size")); - setup_ram_slot("RAM_DIMM_2", 0x56, GET_INT_PROP("rambank2_size")); - setup_ram_slot("RAM_DIMM_3", 0x55, GET_INT_PROP("rambank3_size")); + setup_ram_slot("RAM_DIMM_1", 0x57, GET_INT_PROP("rambank1_size")); + setup_ram_slot("RAM_DIMM_2", 0x56, GET_INT_PROP("rambank2_size")); + setup_ram_slot("RAM_DIMM_3", 0x55, GET_INT_PROP("rambank3_size")); /* register ATI 3D Rage Pro video card with the PCI host bridge */ gMachineObj->add_component("ATIRage", @@ -102,6 +103,12 @@ int create_gossamer(std::string& id) { /* Init virtual CPU and request MPC750 CPU aka G3 */ ppc_cpu_init(grackle_obj, PPC_VER::MPC750); + /* check for a floppy image to be inserted into the virtual superdrive */ + std::string fdd_path = GET_STR_PROP("fdd_img"); + if (!fdd_path.empty()) { + open_floppy_image(fdd_path.c_str()); + } + LOG_F(INFO, "Initialization complete.\n"); return 0; diff --git a/machines/machinepdm.cpp b/machines/machinepdm.cpp index eb365f0..c1d1fdb 100644 --- a/machines/machinepdm.cpp +++ b/machines/machinepdm.cpp @@ -26,6 +26,7 @@ along with this program. If not, see . #include #include +#include #include #include #include @@ -86,6 +87,12 @@ int create_pdm(std::string& id) { /* Init virtual CPU and request MPC601 */ ppc_cpu_init(hmc_obj, PPC_VER::MPC601); + /* check for a floppy image to be inserted into the virtual superdrive */ + std::string fdd_path = GET_STR_PROP("fdd_img"); + if (!fdd_path.empty()) { + open_floppy_image(fdd_path.c_str()); + } + LOG_F(INFO, "Initialization completed.\n"); return 0;