mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-06-27 05:29:32 +00:00
Initial support for floppy disk images.
This commit is contained in:
parent
decb8342be
commit
f39188beb1
|
@ -9,6 +9,7 @@ file(GLOB SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/common/pci/*.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/common/scsi/*.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/ethernet/*.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/floppy/*.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/ioctrl/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/ioctrl/*.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/memctrl/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/memctrl/*.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/serial/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/serial/*.cpp"
|
||||||
|
|
208
devices/floppy/floppyimg.cpp
Normal file
208
devices/floppy/floppyimg.cpp
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file Support for reading and writing of various floppy images. */
|
||||||
|
|
||||||
|
#include "floppyimg.h"
|
||||||
|
#include <loguru.hpp>
|
||||||
|
#include <memaccess.h>
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
72
devices/floppy/floppyimg.h
Normal file
72
devices/floppy/floppyimg.h
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file Definitions for working with various floppy images. */
|
||||||
|
|
||||||
|
#ifndef FLOPPY_IMG_H
|
||||||
|
#define FLOPPY_IMG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -76,6 +76,8 @@ static const PropMap GossamerSettings = {
|
||||||
new IntProperty( 0, vector<uint32_t>({0, 8, 16, 32, 64, 128, 256}))},
|
new IntProperty( 0, vector<uint32_t>({0, 8, 16, 32, 64, 128, 256}))},
|
||||||
{"gfxmem_size",
|
{"gfxmem_size",
|
||||||
new IntProperty( 2, vector<uint32_t>({2, 4, 6}))},
|
new IntProperty( 2, vector<uint32_t>({2, 4, 6}))},
|
||||||
|
{"fdd_img",
|
||||||
|
new StrProperty("")},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const PropMap PDMSettings = {
|
static const PropMap PDMSettings = {
|
||||||
|
@ -83,6 +85,8 @@ static const PropMap PDMSettings = {
|
||||||
new IntProperty(0, vector<uint32_t>({0, 8, 16, 32, 64, 128}))},
|
new IntProperty(0, vector<uint32_t>({0, 8, 16, 32, 64, 128}))},
|
||||||
{"rambank2_size",
|
{"rambank2_size",
|
||||||
new IntProperty(0, vector<uint32_t>({0, 8, 16, 32, 64, 128}))},
|
new IntProperty(0, vector<uint32_t>({0, 8, 16, 32, 64, 128}))},
|
||||||
|
{"fdd_img",
|
||||||
|
new StrProperty("")},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const map<string, string> PropHelp = {
|
static const map<string, string> PropHelp = {
|
||||||
|
@ -90,6 +94,7 @@ static const map<string, string> PropHelp = {
|
||||||
{"rambank2_size", "specifies RAM bank 2 size in MB"},
|
{"rambank2_size", "specifies RAM bank 2 size in MB"},
|
||||||
{"rambank3_size", "specifies RAM bank 3 size in MB"},
|
{"rambank3_size", "specifies RAM bank 3 size in MB"},
|
||||||
{"gfxmem_size", "specifies video memory size in MB"},
|
{"gfxmem_size", "specifies video memory size in MB"},
|
||||||
|
{"fdd_img", "specifies path to floppy disk image"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const map<string, tuple<PropMap, function<int(string&)>, string>> machines = {
|
static const map<string, tuple<PropMap, function<int(string&)>, string>> machines = {
|
||||||
|
|
|
@ -26,6 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include <cpu/ppc/ppcemu.h>
|
#include <cpu/ppc/ppcemu.h>
|
||||||
#include <devices/common/machineid.h>
|
#include <devices/common/machineid.h>
|
||||||
|
#include <devices/floppy/floppyimg.h>
|
||||||
#include <devices/ioctrl/macio.h>
|
#include <devices/ioctrl/macio.h>
|
||||||
#include <devices/memctrl/mpc106.h>
|
#include <devices/memctrl/mpc106.h>
|
||||||
#include <devices/memctrl/spdram.h>
|
#include <devices/memctrl/spdram.h>
|
||||||
|
@ -89,9 +90,9 @@ int create_gossamer(std::string& id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* configure RAM slots */
|
/* configure RAM slots */
|
||||||
setup_ram_slot("RAM_DIMM_1", 0x57, GET_INT_PROP("rambank1_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_2", 0x56, GET_INT_PROP("rambank2_size"));
|
||||||
setup_ram_slot("RAM_DIMM_3", 0x55, GET_INT_PROP("rambank3_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 */
|
/* register ATI 3D Rage Pro video card with the PCI host bridge */
|
||||||
gMachineObj->add_component("ATIRage",
|
gMachineObj->add_component("ATIRage",
|
||||||
|
@ -102,6 +103,12 @@ int create_gossamer(std::string& id) {
|
||||||
/* Init virtual CPU and request MPC750 CPU aka G3 */
|
/* Init virtual CPU and request MPC750 CPU aka G3 */
|
||||||
ppc_cpu_init(grackle_obj, PPC_VER::MPC750);
|
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");
|
LOG_F(INFO, "Initialization complete.\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -26,6 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include <cpu/ppc/ppcemu.h>
|
#include <cpu/ppc/ppcemu.h>
|
||||||
#include <devices/common/machineid.h>
|
#include <devices/common/machineid.h>
|
||||||
|
#include <devices/floppy/floppyimg.h>
|
||||||
#include <devices/ioctrl/amic.h>
|
#include <devices/ioctrl/amic.h>
|
||||||
#include <devices/memctrl/hmc.h>
|
#include <devices/memctrl/hmc.h>
|
||||||
#include <devices/sound/soundserver.h>
|
#include <devices/sound/soundserver.h>
|
||||||
|
@ -86,6 +87,12 @@ int create_pdm(std::string& id) {
|
||||||
/* Init virtual CPU and request MPC601 */
|
/* Init virtual CPU and request MPC601 */
|
||||||
ppc_cpu_init(hmc_obj, PPC_VER::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");
|
LOG_F(INFO, "Initialization completed.\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user