summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats
diff options
context:
space:
mode:
author npwoods <npwoods@mess.org>2022-04-07 08:41:22 -0400
committer GitHub <noreply@github.com>2022-04-07 14:41:22 +0200
commitefa6a9a11fed6b9301fe1b2339668bc2a09aa467 (patch)
treedcd35b63da8b882a01ed3d57b839a9ea2fab60f2 /src/lib/formats
parentb7e654b28d0432ed8e8afda9a30435fb07b7305d (diff)
Adding support for formatting CoCo OS-9 file systems (#9434)
* Adding support for formatting CoCo OS-9 file systems A caveat of this support is that there is no way for a FS implementation to get the actual floppy geometry. Therefore, we are currently hard coding the track count, head count and sector size
Diffstat (limited to 'src/lib/formats')
-rw-r--r--src/lib/formats/fs_coco_os9.cpp171
-rw-r--r--src/lib/formats/fs_coco_os9.h5
2 files changed, 168 insertions, 8 deletions
diff --git a/src/lib/formats/fs_coco_os9.cpp b/src/lib/formats/fs_coco_os9.cpp
index d203f731ba8..9bae816dfea 100644
--- a/src/lib/formats/fs_coco_os9.cpp
+++ b/src/lib/formats/fs_coco_os9.cpp
@@ -9,7 +9,7 @@
OS-9 Level 2 Technical Reference, Chapter 5, Random Block File Manager,
page 2
- http://www.colorcomputerarchive.com/coco/Documents/Manuals/Operating Systems/OS-9 Level 2 Manual (Tandy).pdf
+ https://colorcomputerarchive.com/repo/Documents/Manuals/Operating%20Systems/OS-9%20Level%202%20Manual%20(Tandy).pdf
***************************************************************************/
@@ -66,7 +66,7 @@ void coco_os9_image::enumerate_f(floppy_enumerator &fe, u32 form_factor, const s
bool coco_os9_image::can_format() const
{
- return false;
+ return true;
}
@@ -118,6 +118,7 @@ std::vector<meta_description> coco_os9_image::volume_meta_description() const
{
std::vector<meta_description> results;
results.emplace_back(meta_description(meta_name::name, meta_type::string, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 32; }, "Volume name, up to 32 characters"));
+ results.emplace_back(meta_description(meta_name::creation_date, meta_type::date, util::arbitrary_datetime::now(), false, nullptr, "Creation time"));
return results;
}
@@ -201,6 +202,22 @@ std::string coco_os9_image::pick_os9_string(std::string_view raw_string)
//-------------------------------------------------
+// to_os9_string
+//-------------------------------------------------
+
+std::string coco_os9_image::to_os9_string(std::string_view s, size_t length)
+{
+ std::string result(length, '\0');
+ for (auto i = 0; i < std::min(length, s.size()); i++)
+ {
+ result[i] = (s[i] & 0x7F)
+ | (i == s.size() ? 0x80 : 0x00);
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
// pick_integer_be
//-------------------------------------------------
@@ -214,6 +231,38 @@ u32 coco_os9_image::pick_integer_be(const u8 *data, int length)
//-------------------------------------------------
+// from_os9_date
+//-------------------------------------------------
+
+util::arbitrary_datetime coco_os9_image::from_os9_date(u32 os9_date, u16 os9_time)
+{
+ util::arbitrary_datetime dt;
+ memset(&dt, 0, sizeof(dt));
+ dt.year = ((os9_date >> 16) & 0xFF) + 1900;
+ dt.month = (os9_date >> 8) & 0xFF;
+ dt.day_of_month = (os9_date >> 0) & 0xFF;
+ dt.hour = (os9_time >> 8) & 0xFF;
+ dt.minute = (os9_time >> 0) & 0xFF;
+ return dt;
+}
+
+
+//-------------------------------------------------
+// to_os9_date
+//-------------------------------------------------
+
+std::tuple<u32, u16> coco_os9_image::to_os9_date(const util::arbitrary_datetime &datetime)
+{
+ u32 os9_date = ((datetime.year - 1900) & 0xFF) << 16
+ | (datetime.month & 0xFF) << 8
+ | (datetime.day_of_month & 0xFF) << 0;
+ u16 os9_time = (datetime.hour & 0xFF) << 8
+ | (datetime.minute & 0xFF) << 0;
+ return std::make_tuple(os9_date, os9_time);
+}
+
+
+//-------------------------------------------------
// validate_filename
//-------------------------------------------------
@@ -276,12 +325,7 @@ coco_os9_image::file_header::file_header(fsblk_t::block_t &&block)
util::arbitrary_datetime coco_os9_image::file_header::creation_date() const
{
- util::arbitrary_datetime dt;
- memset(&dt, 0, sizeof(dt));
- dt.year = 1900 + m_block.r8(13);
- dt.month = m_block.r8(14);
- dt.day_of_month = m_block.r8(15);
- return dt;
+ return from_os9_date(m_block.r24b(13));
}
@@ -351,6 +395,7 @@ meta_data coco_os9_image::impl::metadata()
{
meta_data results;
results.set(meta_name::name, m_volume_header.name());
+ results.set(meta_name::creation_date, m_volume_header.creation_date());
return results;
}
@@ -378,6 +423,116 @@ void coco_os9_image::impl::drop_root_ref()
//-------------------------------------------------
+// impl::format
+//-------------------------------------------------
+
+void coco_os9_image::impl::format(const meta_data &meta)
+{
+ // for some reason, the OS-9 world favored filling with 0xE5
+ m_blockdev.fill(0xE5);
+
+ // identify geometry info
+ u8 sectors = 18; // TODO - we need a definitive technique to get the floppy geometry
+ u8 heads = 1; // TODO - we need a definitive technique to get the floppy geometry
+ u16 sector_bytes = 256; // TODO - we need a definitive technique to get the floppy geometry
+ bool is_double_density = true; // TODO - we need a definitive technique to get the floppy geometry
+ u32 tracks = m_blockdev.block_count() / sectors / heads;
+
+ // get attributes from metadata
+ std::string volume_title = meta.get_string(meta_name::name, "UNTITLED");
+ util::arbitrary_datetime creation_datetime = meta.get_date(meta_name::creation_date);
+ auto [creation_os9date, creation_os9time] = to_os9_date(creation_datetime);
+
+ u32 lsn_count = m_blockdev.block_count();
+ u16 cluster_size = 1;
+ u16 owner_id = 1;
+ u16 disk_id = 1;
+ u8 attributes = 0;
+ u32 allocation_bitmap_bits = lsn_count / cluster_size;
+ u32 allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes;
+ u8 format_flags = ((heads > 1) ? 0x01 : 0x00)
+ | (is_double_density ? 0x02 : 0x00);
+
+ // volume header
+ auto volume_header = m_blockdev.get(0);
+ volume_header.fill(0x00);
+ volume_header.w24b(0, lsn_count); // DD.TOT - total secctors
+ volume_header.w8(3, sectors); // DD.TKS - track size in sectors
+ volume_header.w16b(4, (allocation_bitmap_bits + 7) / 8); // DD.MAP - allocation bitmap in bytes
+ volume_header.w16b(6, cluster_size); // DD.BIT - cluster size
+ volume_header.w24b(8, 1 + allocation_bitmap_lsns); // DD.DIR - root directory LSN
+ volume_header.w16b(11, owner_id); // DD.OWN - owner ID
+ volume_header.w8(13, attributes); // DD.ATT - Dattributes
+ volume_header.w16b(14, disk_id); // DD.DSK - disk ID
+ volume_header.w8(16, format_flags); // DD.FMT - format flags
+ volume_header.w16b(17, sectors); // DD.SPT - sectors per track
+ volume_header.w24b(26, creation_os9date); // DD.DAT - date of creation
+ volume_header.w16b(29, creation_os9time); // DD.DAT - time of creation
+ volume_header.wstr(31, to_os9_string(volume_title, 32)); // DD.NAM - title
+ volume_header.w16b(103, sector_bytes / 256); // sector bytes
+
+ // path descriptor options
+ volume_header.w8(0x3f + 0x00, 1); // device class
+ volume_header.w8(0x3f + 0x01, 1); // drive number
+ volume_header.w8(0x3f + 0x03, 0x20); // device type
+ volume_header.w8(0x3f + 0x04, 1); // density capability
+ volume_header.w16b(0x3f + 0x05, tracks); // number of tracks
+ volume_header.w8(0x3f + 0x07, heads); // number of sides
+ volume_header.w16b(0x3f + 0x09, sectors); // sectors per track
+ volume_header.w16b(0x3f + 0x0b, sectors); // sectors on track zero
+ volume_header.w8(0x3f + 0x0d, 3); // sector interleave factor
+ volume_header.w8(0x3f + 0x0e, 8); // default sectors per allocation
+
+ // allocation bitmap
+ u32 total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 8;
+ std::vector<u8> abblk_bytes;
+ abblk_bytes.resize(sector_bytes);
+ for (u32 i = 0; i < allocation_bitmap_lsns; i++)
+ {
+ for (u32 j = 0; j < sector_bytes; j++)
+ {
+ u32 pos = (i * sector_bytes + j) * 8;
+ if (pos + 8 < total_allocated_sectors)
+ abblk_bytes[j] = 0xFF;
+ else if (pos >= total_allocated_sectors)
+ abblk_bytes[j] = 0x00;
+ else
+ abblk_bytes[j] = ~((1 << (8 - total_allocated_sectors + pos)) - 1);
+ }
+
+ auto abblk = m_blockdev.get(1 + i);
+ abblk.copy(0, abblk_bytes.data(), sector_bytes);
+ }
+
+ // root directory header
+ auto roothdr_blk = m_blockdev.get(1 + allocation_bitmap_lsns);
+ roothdr_blk.fill(0x00);
+ roothdr_blk.w8(0x00, 0xBF);
+ roothdr_blk.w8(0x01, 0x00);
+ roothdr_blk.w8(0x02, 0x00);
+ roothdr_blk.w24b(0x03, creation_os9date);
+ roothdr_blk.w16b(0x06, creation_os9time);
+ roothdr_blk.w8(0x08, 0x01);
+ roothdr_blk.w8(0x09, 0x00);
+ roothdr_blk.w8(0x0A, 0x00);
+ roothdr_blk.w8(0x0B, 0x00);
+ roothdr_blk.w8(0x0C, 0x40);
+ roothdr_blk.w24b(0x0D, creation_os9date);
+ roothdr_blk.w24b(0x10, 1 + allocation_bitmap_lsns + 1);
+ roothdr_blk.w16b(0x13, 8);
+
+ // root directory data
+ auto rootdata_blk = m_blockdev.get(1 + allocation_bitmap_lsns + 1);
+ rootdata_blk.fill(0x00);
+ rootdata_blk.w8(0x00, 0x2E);
+ rootdata_blk.w8(0x01, 0xAE);
+ rootdata_blk.w8(0x1F, 1 + allocation_bitmap_lsns);
+ rootdata_blk.w8(0x20, 0xAE);
+ rootdata_blk.w8(0x3F, 1 + allocation_bitmap_lsns);
+}
+
+
+//-------------------------------------------------
// impl::open_directory
//-------------------------------------------------
diff --git a/src/lib/formats/fs_coco_os9.h b/src/lib/formats/fs_coco_os9.h
index 57bfce983c6..422dd101eb2 100644
--- a/src/lib/formats/fs_coco_os9.h
+++ b/src/lib/formats/fs_coco_os9.h
@@ -39,6 +39,7 @@ public:
u16 sectors_per_track() const { return m_block.r16b(17); }
u32 bootstrap_lsn() const { return m_block.r24b(21); }
u16 bootstrap_size() const { return m_block.r16b(24); }
+ util::arbitrary_datetime creation_date() const { return from_os9_date(m_block.r24b(26), m_block.r16b(29)); }
u16 sector_size() const { u16 result = m_block.r16b(104); return result != 0 ? result : 256; }
u8 sides() const { return (format_flags() & 0x01) ? 2 : 1; }
bool double_density() const { return (format_flags() & 0x02) != 0; }
@@ -137,6 +138,7 @@ private:
virtual meta_data metadata() override;
virtual dir_t root() override;
+ virtual void format(const meta_data &meta) override;
private:
volume_header m_volume_header;
@@ -148,6 +150,9 @@ private:
};
static std::string pick_os9_string(std::string_view raw_string);
+ static std::string to_os9_string(std::string_view s, size_t length);
+ static util::arbitrary_datetime from_os9_date(u32 os9_date, u16 os9_time = 0);
+ static std::tuple<u32, u16> to_os9_date(const util::arbitrary_datetime &datetime);
static u32 pick_integer_be(const u8 *data, int length);
static bool validate_filename(std::string_view name);
static bool is_ignored_filename(std::string_view name);