From 0ce06a9d748f59455655f004a1d92f5e03d1ee5c Mon Sep 17 00:00:00 2001 From: Sven Schnelle <41547105+svenschnelle@users.noreply.github.com> Date: Sat, 24 Dec 2022 20:33:59 +0100 Subject: formats/hp300_dsk.cpp, formats/hp_lif.cpp: Added HP 300 floppy format and LIF filesystem. (#10729) Added support for various disk formats used with HP computers. Named 'hp300', but also contains formats used with HP85 and HP150 computers, using the same floppy drivers as the 300 series. Most of the information was taken from "HP Flexible Disk Formats" by Martin Hepperle. HP LIF was used in quite a lot of different HP products like the HP 9000/300, HP85/87 and HP150 computers. Added support for reading this filesystem. --- scripts/src/formats.lua | 24 ++ src/lib/formats/all.cpp | 14 ++ src/lib/formats/fs_hplif.cpp | 494 ++++++++++++++++++++++++++++++++++++++++++ src/lib/formats/fs_hplif.h | 47 ++++ src/lib/formats/hp300_dsk.cpp | 48 ++++ src/lib/formats/hp300_dsk.h | 32 +++ 6 files changed, 659 insertions(+) create mode 100644 src/lib/formats/fs_hplif.cpp create mode 100644 src/lib/formats/fs_hplif.h create mode 100644 src/lib/formats/hp300_dsk.cpp create mode 100644 src/lib/formats/hp300_dsk.h diff --git a/scripts/src/formats.lua b/scripts/src/formats.lua index 132a33ec2ce..0fc21bb1941 100644 --- a/scripts/src/formats.lua +++ b/scripts/src/formats.lua @@ -1091,6 +1091,18 @@ if opt_tool(FORMATS, "HTI_TAP") then } end +-------------------------------------------------- +-- +--@src/lib/formats/hp_ipc_dsk.h,FORMATS["HP300_DSK"] = true +-------------------------------------------------- + +if opt_tool(FORMATS, "HP300_DSK") then + files { + MAME_DIR.. "src/lib/formats/hp300_dsk.cpp", + MAME_DIR.. "src/lib/formats/hp300_dsk.h", + } +end + -------------------------------------------------- -- --@src/lib/formats/hpi_dsk.h,FORMATS["HPI_DSK"] = true @@ -2172,6 +2184,18 @@ if opt_tool(FORMATS, "FS_FAT") then } end +-------------------------------------------------- +-- +--@src/lib/formats/fs_hplif.h,FORMATS["FS_HPLIF"] = true +-------------------------------------------------- + +if opt_tool(FORMATS, "FS_HPLIF") then + files { + MAME_DIR.. "src/lib/formats/fs_hplif.cpp", + MAME_DIR.. "src/lib/formats/fs_hplif.h", + } +end + -------------------------------------------------- -- --@src/lib/formats/fs_oric_jasmin.h,FORMATS["FS_ORIC_JASMIN"] = true diff --git a/src/lib/formats/all.cpp b/src/lib/formats/all.cpp index 98acf68d677..b8d0089f3f9 100644 --- a/src/lib/formats/all.cpp +++ b/src/lib/formats/all.cpp @@ -336,6 +336,10 @@ #include "hti_tape.h" #endif +#ifdef HAS_FORMATS_HP300_DSK +#include "hp300_dsk.h" +#endif + #ifdef HAS_FORMATS_HPI_DSK #include "hpi_dsk.h" #endif @@ -720,6 +724,10 @@ #include "fs_fat.h" #endif +#ifdef HAS_FORMATS_FS_HPLIF +#include "fs_hplif.h" +#endif + #ifdef HAS_FORMATS_FS_ISIS #include "fs_isis.h" #endif @@ -867,12 +875,18 @@ void mame_formats_full_list(mame_formats_enumerator &en) #ifdef HAS_FORMATS_APOLLO_DSK en.add(FLOPPY_APOLLO_FORMAT); // apollo_dsk.h #endif +#ifdef HAS_FORMATS_HP300_DSK + en.add(FLOPPY_HP300_FORMAT); // hp300_dsk.h +#endif #ifdef HAS_FORMATS_HP_IPC_DSK en.add(FLOPPY_HP_IPC_FORMAT); // hp_ipc_dsk.h #endif #ifdef HAS_FORMATS_HPI_DSK en.add(FLOPPY_HPI_FORMAT); // hpi_dsk.h #endif +#ifdef HAS_FORMATS_FS_HPLIF + en.add(fs::HPLIF); // fs_lif.h +#endif en.category("Applix"); #ifdef HAS_FORMATS_APPLIX_DSK diff --git a/src/lib/formats/fs_hplif.cpp b/src/lib/formats/fs_hplif.cpp new file mode 100644 index 00000000000..018c4ba5e21 --- /dev/null +++ b/src/lib/formats/fs_hplif.cpp @@ -0,0 +1,494 @@ +// license:BSD-3-Clause +// copyright-holders:Sven Schnelle +/*************************************************************************** + + fs_hplif.cpp + + Management of HP LIF images + +***************************************************************************/ + +#include "fs_hplif.h" +#include "hp300_dsk.h" +#include "corestr.h" +#include "osdcomm.h" +#include "strformat.h" + +#include +#include +#include +#include +#include +namespace fs { + const hplif_image HPLIF; +}; + +using namespace fs; + +namespace { + +class impl : public filesystem_t { +public: + + struct hplif_time { + u8 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; + }; + + struct hplif_dirent + { + char m_file_name[10]; + u16 m_file_type; + u32 m_starting_sector; + u32 m_sector_count; + hplif_time m_time; + u16 m_volume_number; + u32 m_general_purpose; + }; + + class block_iterator + { + public: + block_iterator(const impl &fs, u32 first_sector, u32 sector_count); + bool next(); + const void *data() const; + const std::array &dirent_data() const; + u8 size() const; + + private: + const impl & m_fs; + fsblk_t::block_t m_block; + u8 m_sector; + u32 m_sector_count; + }; + + impl(fsblk_t &blockdev); + virtual ~impl() = default; + + virtual meta_data volume_metadata() override; + virtual std::pair metadata(const std::vector &path) override; + virtual std::pair> directory_contents(const std::vector &path) override; + virtual std::pair> file_read(const std::vector &path) override; + +private: + fsblk_t::block_t read_sector(u32 starting_sector) const; + std::optional dirent_from_path(const std::vector &path) const; + void iterate_directory_entries(const std::function &callback) const; + util::arbitrary_datetime decode_datetime(const hplif_time *time) const; + meta_data metadata_from_dirent(const hplif_dirent &dirent) const; + err_t format(const meta_data &meta) override; +}; + +// methods +std::string_view strtrimright_hplif(std::string_view str); +template std::string_view strtrimright_hplif(const char (&str)[N]); + +} // anonymous namespace + + +//------------------------------------------------- +// name +//------------------------------------------------- + +const char *fs::hplif_image::name() const +{ + return "hplif"; +} + + +//------------------------------------------------- +// description +//------------------------------------------------- + +const char *fs::hplif_image::description() const +{ + return "HPLIF"; +} + + +//------------------------------------------------- +// enumerate_f +//------------------------------------------------- + +void fs::hplif_image::enumerate_f(floppy_enumerator &fe) const +{ + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 630784, "hp_lif_9121_format_1", "HP 9212 LIF 3.5\" dual-sided double density Format 1"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 709632, "hp_lif_9121_format_2", "HP 9121 LIF 3.5\" dual-sided double density Format 2"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 788480, "hp_lif_9121_format_3", "HP 9121 LIF 3.5\" dual-sided double density Format 3"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::SSDD, 286720, "hp_lif_9121_format_4", "HP 9121 LIF 3.5\" single-sided double density Format 4"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 737280, "hp_lif_9121_format_16", "HP 9121 LIF 3.5\" dual-sided double density Format 16"); + + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1261568, "hp_lif_9122_format_014", "HP 9122 LIF 3.5\" dual-sided high density Format 0, 1, 4"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1419264, "hp_lif_9122_format_2", "HP 9122 LIF 3.5\" dual-sided high density Format 2"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1576960, "hp_lif_9122_format_3", "HP 9122 LIF 3.5\" dual-sided high density Format 3"); + fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1474560, "hp_lif_9122_format_16" , "HP 9122 LIF 3.5\" dual-sided high density Format 16"); +} + + +//------------------------------------------------- +// can_format +//------------------------------------------------- + +bool fs::hplif_image::can_format() const +{ + return true; +} + + +//------------------------------------------------- +// can_read +//------------------------------------------------- + +bool fs::hplif_image::can_read() const +{ + return true; +} + + +//------------------------------------------------- +// can_write +//------------------------------------------------- + +bool fs::hplif_image::can_write() const +{ + return false; +} + + +//------------------------------------------------- +// has_rsrc +//------------------------------------------------- + +bool fs::hplif_image::has_rsrc() const +{ + return false; +} + + +//------------------------------------------------- +// volume_meta_description +//------------------------------------------------- + +std::vector fs::hplif_image::volume_meta_description() const +{ + std::vector results; + results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 6; }, "Volume name, up to 6 characters"); + results.emplace_back(meta_name::creation_date, 0, true, nullptr, "Time of creation"); + return results; +} + + +//------------------------------------------------- +// file_meta_description +//------------------------------------------------- + +std::vector fs::hplif_image::file_meta_description() const +{ + std::vector results; + results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 10; }, "File name, up to 10 characters"); + results.emplace_back(meta_name::file_type, "", true, nullptr, "Type of the file"); + results.emplace_back(meta_name::length, 0, true, nullptr, "Size of the file in bytes"); + results.emplace_back(meta_name::modification_date, 0, true, nullptr, "Time of last modification"); + return results; +} + + +//------------------------------------------------- +// mount +//------------------------------------------------- + +std::unique_ptr fs::hplif_image::mount(fsblk_t &blockdev) const +{ + return std::make_unique(blockdev); +} + + +//------------------------------------------------- +// strtrimright_cbm +//------------------------------------------------- + +namespace { + +std::string_view strtrimright_hplif(std::string_view str) +{ + return strtrimright(str, [](char c) { return c != (char)0x20; }); +} + + +//------------------------------------------------- +// strtrimright_cbm +//------------------------------------------------- + +template +std::string_view strtrimright_hplif(const char (&str)[N]) +{ + std::string_view sv(str, std::size(str)); + return strtrimright_hplif(sv); +} + + +//------------------------------------------------- +// impl ctor +//------------------------------------------------- + +impl::impl(fsblk_t &blockdev) + : filesystem_t(blockdev, 256) +{ +} + + +//------------------------------------------------- +// impl::volume_metadata +//------------------------------------------------- + +meta_data impl::volume_metadata() +{ + auto block = read_sector(0); + std::string_view disk_name = std::string_view((const char *) block.rodata() + 2, 6); + + meta_data results; + results.set(meta_name::name, strtrimright_hplif(disk_name)); + results.set(meta_name::creation_date, decode_datetime(reinterpret_cast(block.rodata() + 36))); + return results; +} + + +//------------------------------------------------- +// impl::metadata +//------------------------------------------------- + +std::pair impl::metadata(const std::vector &path) +{ + std::optional dirent = dirent_from_path(path); + if (!dirent) + return std::make_pair(ERR_NOT_FOUND, meta_data()); + + return std::make_pair(ERR_OK, metadata_from_dirent(*dirent)); +} + + +//------------------------------------------------- +// impl::directory_contents +//------------------------------------------------- + +std::pair> impl::directory_contents(const std::vector &path) +{ + std::vector results; + auto callback = [this, &results](const hplif_dirent &ent) + { + results.emplace_back(dir_entry_type::file, metadata_from_dirent(ent)); + return false; + }; + iterate_directory_entries(callback); + return std::make_pair(ERR_OK, std::move(results)); +} + + +//------------------------------------------------- +// impl::file_read +//------------------------------------------------- + +std::pair> impl::file_read(const std::vector &path) +{ + // find the file + std::optional dirent = dirent_from_path(path); + if (!dirent) + return std::make_pair(ERR_NOT_FOUND, std::vector()); + + // and get the data + u32 sector_count = big_endianize_int32(dirent->m_sector_count); + hplif_dirent hdr = dirent.value(); + std::vector result; + result.insert(result.end(), + reinterpret_cast(&hdr), + reinterpret_cast(&hdr) + 32); + result.reserve(sector_count * 256 + 32); + block_iterator iter(*this, big_endianize_int32(dirent->m_starting_sector), sector_count); + while (iter.next()) + result.insert(result.end(), (const u8 *)iter.data(), (const u8 *)iter.data() + 256); + + return std::make_pair(ERR_OK, std::move(result)); +} + + +//------------------------------------------------- +// impl::read_sector +//------------------------------------------------- + +fsblk_t::block_t impl::read_sector(u32 sector) const +{ + return m_blockdev.get(sector); +} + + +//------------------------------------------------- +// impl::dirent_from_path +//------------------------------------------------- + +std::optional impl::dirent_from_path(const std::vector &path) const +{ + if (path.size() != 1) + return { }; + std::string_view path_part = path[0]; + + std::optional result; + auto callback = [&result, path_part](const hplif_dirent &dirent) + { + bool found = strtrimright_hplif(dirent.m_file_name) == path_part; + if (found) + result = dirent; + return found; + }; + iterate_directory_entries(callback); + return result; +} + + +//------------------------------------------------- +// impl::iterate_directory_entries +//------------------------------------------------- + +void impl::iterate_directory_entries(const std::function &callback) const +{ + fsblk_t::block_t block = m_blockdev.get(0); + block_iterator iter(*this, block.r32b(8), block.r32b(16)); + + if (block.r16b(0) != 0x8000) + { + return; + } + + while (iter.next()) + { + for (const hplif_dirent &ent : iter.dirent_data()) + { + if (ent.m_file_type == 0xffff) + { + return; + } + + if (ent.m_file_type != 0x0000) + { + if (callback(ent)) + return; + } + } + } +} + +//------------------------------------------------- +// impl::decode_datetime +//------------------------------------------------- + +util::arbitrary_datetime impl::decode_datetime(const hplif_time *time) const +{ + util::arbitrary_datetime result; + memset(&result, 0, sizeof(result)); + + result.year = bcd_2_dec(time->year) + 1900; + result.month = bcd_2_dec(time->month); + result.day_of_month = bcd_2_dec(time->day); + result.hour = bcd_2_dec(time->hour); + result.minute = bcd_2_dec(time->minute); + result.second = bcd_2_dec(time->second); + return result; +} + +//------------------------------------------------- +// impl::metadata_from_dirent +//------------------------------------------------- + +meta_data impl::metadata_from_dirent(const hplif_dirent &dirent) const +{ + std::string file_type = util::string_format("0x%04X", big_endianize_int16(dirent.m_file_type)); + + // build the metadata and return it + meta_data result; + result.set(meta_name::name, strtrimright_hplif(dirent.m_file_name)); + result.set(meta_name::file_type, std::move(file_type)); + result.set(meta_name::length, big_endianize_int32(dirent.m_sector_count) * 256); + result.set(meta_name::modification_date, decode_datetime(&dirent.m_time)); + return result; +} + + +//------------------------------------------------- +// impl::block_iterator ctor +//------------------------------------------------- + +impl::block_iterator::block_iterator(const impl &fs, u32 starting_sector, u32 sector_count) + : m_fs(fs) + , m_sector(starting_sector) + , m_sector_count(sector_count) +{ +} + + +//------------------------------------------------- +// impl::block_iterator::next +//------------------------------------------------- + +bool impl::block_iterator::next() +{ + bool result; + if (m_sector_count != 0x00) + { + m_block = m_fs.read_sector(m_sector++); + m_sector_count--; + result = true; + } + else + { + // the iterator has already completed + result = false; + } + return result; +} + + +//------------------------------------------------- +// impl::block_iterator::data +//------------------------------------------------- + +const void *impl::block_iterator::data() const +{ + return m_block.rodata(); +} + +//------------------------------------------------- +// impl::format +//------------------------------------------------- + +err_t impl::format(const meta_data &meta) +{ + std::string volume_name = meta.get_string(meta_name::name, "B9826 "); + fsblk_t::block_t block = m_blockdev.get(0); + + if (volume_name.size() < 6) + volume_name.insert(volume_name.end(), 6 - volume_name.size(), ' '); + if (volume_name.size() > 6) + volume_name.resize(6); + + block.w16b(0, 0x8000); // LIF magic + block.wstr(2, volume_name); + block.w32b(8, 2); // directory start + block.w16b(12, 0x1000); // LIF identifier + block.w32b(16, 14); // directory size + block.w16b(20, 1); // LIF version + return ERR_OK; +} + +//------------------------------------------------- +// impl::block_iterator::dirent_data +//------------------------------------------------- + +const std::array &impl::block_iterator::dirent_data() const +{ + return *reinterpret_cast *>(m_block.rodata()); +} + +} // anonymous namespace diff --git a/src/lib/formats/fs_hplif.h b/src/lib/formats/fs_hplif.h new file mode 100644 index 00000000000..57d151db26c --- /dev/null +++ b/src/lib/formats/fs_hplif.h @@ -0,0 +1,47 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + fs_hplif.h + + Management of CBM (Commodore) DOS disk images + +***************************************************************************/ + +#ifndef MAME_FORMATS_FS_HPLIF_H +#define MAME_FORMATS_FS_HPLIF_H + +#pragma once + + +#include "fsmgr.h" +#include +#include + +namespace fs { + + class hplif_image : public manager_t { + public: + hplif_image() = default; + + virtual const char *name() const override; + virtual const char *description() const override; + + virtual void enumerate_f(floppy_enumerator &fe) const override; + virtual std::unique_ptr mount(fsblk_t &blockdev) const override; + + virtual bool can_format() const override; + virtual bool can_read() const override; + virtual bool can_write() const override; + virtual bool has_rsrc() const override; + + virtual std::vector volume_meta_description() const override; + virtual std::vector file_meta_description() const override; + }; + + + extern const hplif_image HPLIF; + +} // namespace fs + +#endif // #define MAME_FORMATS_FS_HPLIF_H diff --git a/src/lib/formats/hp300_dsk.cpp b/src/lib/formats/hp300_dsk.cpp new file mode 100644 index 00000000000..99f9ac033ee --- /dev/null +++ b/src/lib/formats/hp300_dsk.cpp @@ -0,0 +1,48 @@ +// license:BSD-3-Clause +// copyright-holders:Sven Schnelle +/********************************************************************* + + formats/hp300_dsk.c + + HP 9000/300 disk format + +*********************************************************************/ + +#include "formats/hp300_dsk.h" + +hp300_format::hp300_format() : wd177x_format(formats) +{ +} + +const char *hp300_format::name() const +{ + return "hp300"; +} + +const char *hp300_format::description() const +{ + return "HP 9000/300 disk image"; +} + +const char *hp300_format::extensions() const +{ + return "img"; +} + +const hp300_format::format hp300_format::formats[] = { + // HP 9121S, 9121D or 9133A + { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 16, 77, 2, 256, {}, 1, {}, 50, 22, 54 }, // FORMAT 0,1 + { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 9, 77, 2, 512, {}, 1, {}, 50, 22, 89 }, // FORMAT 2 + { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 5, 77, 2, 1024, {}, 1, {}, 50, 22, 108 }, // FORMAT 3 + { floppy_image::FF_35, floppy_image::DSSD, floppy_image::MFM, 2000, 16, 70, 1, 256, {}, 1, {}, 32, 22, 46 }, // FORMAT 4 + { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 9, 80, 2, 512, {}, 1, {}, 146, 22, 81 }, // FORMAT 16 + // HP9122C/D, 9123D, 9133D/H/L or 9153A/B + { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 32, 77, 2, 256, {}, 1, {}, 50, 22, 59 }, // FORMAT 0,1,4 + { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 18, 77, 2, 512, {}, 1, {}, 50, 22, 97 }, // FORMAT 2 + { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 10, 77, 2, 1024, {}, 1, {}, 50, 22, 123 }, // FORMAT 3 + { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 18, 80, 2, 512, {}, 1, {}, 146, 22, 98 }, // FORMAT 16 + {} +}; + +const hp300_format FLOPPY_HP300_FORMAT; + diff --git a/src/lib/formats/hp300_dsk.h b/src/lib/formats/hp300_dsk.h new file mode 100644 index 00000000000..b86cd1bc72b --- /dev/null +++ b/src/lib/formats/hp300_dsk.h @@ -0,0 +1,32 @@ +// license:BSD-3-Clause +// copyright-holders:Sven Schnelle +/********************************************************************* + + formats/hp300_dsk.h + + HP9000/300 disk format + +*********************************************************************/ +#ifndef MAME_FORMATS_HP300_DSK_H +#define MAME_FORMATS_HP300_DSK_H + +#pragma once + +#include "wd177x_dsk.h" + +class hp300_format : public wd177x_format +{ +public: + hp300_format(); + + virtual const char *name() const override; + virtual const char *description() const override; + virtual const char *extensions() const override; + +private: + static const format formats[]; +}; + +extern const hp300_format FLOPPY_HP300_FORMAT; + +#endif // MAME_FORMATS_HP300_DSK_H -- cgit v1.2.3