mirror of
https://github.com/holub/mame
synced 2025-05-31 01:51:46 +03:00
vtech: Add floppy formats and fs
This commit is contained in:
parent
ec2bec33c2
commit
9add8034d2
@ -1989,6 +1989,30 @@ if opt_tool(FORMATS, "VT_CAS") then
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
--
|
||||
--@src/lib/formats/vt_dsk.h,FORMATS["VT_DSK"] = true
|
||||
--------------------------------------------------
|
||||
|
||||
if opt_tool(FORMATS, "VT_DSK") then
|
||||
files {
|
||||
MAME_DIR.. "src/lib/formats/vt_dsk.cpp",
|
||||
MAME_DIR.. "src/lib/formats/vt_dsk.h",
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
--
|
||||
--@src/lib/formats/fs_vtech.h,FORMATS["FS_VTECH"] = true
|
||||
--------------------------------------------------
|
||||
|
||||
if opt_tool(FORMATS, "FS_VTECH") then
|
||||
files {
|
||||
MAME_DIR.. "src/lib/formats/fs_vtech.cpp",
|
||||
MAME_DIR.. "src/lib/formats/fs_vtech.h",
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
--
|
||||
--@src/lib/formats/wd177x_dsk.h,FORMATS["WD177X_DSK"] = true
|
||||
|
@ -1154,6 +1154,8 @@ FORMATS["VECTOR06_DSK"] = true
|
||||
FORMATS["VG5K_CAS"] = true
|
||||
FORMATS["VICTOR9K_DSK"] = true
|
||||
FORMATS["VT_CAS"] = true
|
||||
FORMATS["VT_DSK"] = true
|
||||
FORMATS["FS_VTECH"] = true
|
||||
FORMATS["WD177X_DSK"] = true
|
||||
FORMATS["X07_CAS"] = true
|
||||
FORMATS["X1_TAP"] = true
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include "emu.h"
|
||||
#include "floppy.h"
|
||||
#include "formats/vt_dsk.h"
|
||||
#include "formats/fs_vtech.h"
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
@ -50,11 +52,18 @@ static void laser_floppies(device_slot_interface &device)
|
||||
device.option_add("525", FLOPPY_525_VTECH);
|
||||
}
|
||||
|
||||
void vtech_floppy_controller_device::floppy_formats(format_registration &fr)
|
||||
{
|
||||
fr.add(FLOPPY_VTECH_BIN_FORMAT);
|
||||
fr.add(FLOPPY_VTECH_DSK_FORMAT);
|
||||
fr.add(FS_VTECH);
|
||||
}
|
||||
|
||||
void vtech_floppy_controller_device::device_add_mconfig(machine_config &config)
|
||||
{
|
||||
VTECH_MEMEXP_SLOT(config, m_memexp);
|
||||
FLOPPY_CONNECTOR(config, m_floppy0, laser_floppies, "525", floppy_image_device::default_mfm_floppy_formats);
|
||||
FLOPPY_CONNECTOR(config, m_floppy1, laser_floppies, "525", floppy_image_device::default_mfm_floppy_formats);
|
||||
FLOPPY_CONNECTOR(config, m_floppy0, laser_floppies, "525", floppy_formats);
|
||||
FLOPPY_CONNECTOR(config, m_floppy1, laser_floppies, "525", floppy_formats);
|
||||
}
|
||||
|
||||
|
||||
|
@ -48,6 +48,8 @@ private:
|
||||
void update_latching_inverter();
|
||||
void flush_writes(bool keep_margin = false);
|
||||
|
||||
static void floppy_formats(format_registration &fr);
|
||||
|
||||
required_device<vtech_memexp_slot_device> m_memexp;
|
||||
required_device<floppy_connector> m_floppy0, m_floppy1;
|
||||
floppy_image_device *m_floppy;
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
#define PITCH_SEEK_SAMPLES 1
|
||||
|
||||
#define FLUX_SCREEN 0
|
||||
#define FLUX_SCREEN 1
|
||||
|
||||
#define FLOPSND_TAG "floppysound"
|
||||
|
||||
|
@ -653,6 +653,14 @@
|
||||
#include "vt_cas.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_FORMATS_VT_DSK
|
||||
#include "vt_dsk.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_FORMATS_FS_VTECH
|
||||
#include "fs_vtech.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_FORMATS_WD177X_DSK
|
||||
#include "wd177x_dsk.h"
|
||||
#endif
|
||||
@ -1379,6 +1387,13 @@ void mame_formats_full_list(mame_formats_enumerator &en)
|
||||
en.add(vtech1_cassette_formats); // vt_cas.h
|
||||
en.add(vtech2_cassette_formats); // vt_cas.h
|
||||
#endif
|
||||
#ifdef HAS_FORMATS_VT_CAS
|
||||
en.add(FLOPPY_VTECH_BIN_FORMAT); // vt_dsk.h
|
||||
en.add(FLOPPY_VTECH_DSK_FORMAT); // vt_dsk.h
|
||||
#endif
|
||||
#ifdef HAS_FORMATS_FS_VTECH
|
||||
en.add(FS_VTECH); // fs_vtech.h
|
||||
#endif
|
||||
|
||||
en.category("Canon");
|
||||
#ifdef HAS_FORMATS_X07_CAS
|
||||
|
366
src/lib/formats/fs_vtech.cpp
Normal file
366
src/lib/formats/fs_vtech.cpp
Normal file
@ -0,0 +1,366 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
|
||||
// Management of VTech images
|
||||
|
||||
#include "emu.h"
|
||||
#include "fs_vtech.h"
|
||||
#include "vt_dsk.h"
|
||||
|
||||
const fs_vtech FS_VTECH;
|
||||
|
||||
// Floppy only, format is 40 tracks, 1 head, 16 sectors numbered 0-15, 256 bytes/sector.
|
||||
// Filesystem has no subdirectories.
|
||||
//
|
||||
// Track 0 sectors 0-14 have the file names. 16 bytes/entry
|
||||
// offset 0 : File type 'T' (basic) or 'B' (binary)
|
||||
// offset 1 : 0x3a
|
||||
// offset 2-9: File name
|
||||
// offset a : Track number of first file sector
|
||||
// offset b : Sector number of first file sector
|
||||
// offset c-d: Start address (little-endian)
|
||||
// offset e-f: End address (little-endian)
|
||||
//
|
||||
// Files are stored with 126 bytes/sector, and bytes 126 and 127 are
|
||||
// track/sector of the next file data sector.
|
||||
|
||||
const char *fs_vtech::name() const
|
||||
{
|
||||
return "vtech";
|
||||
}
|
||||
|
||||
const char *fs_vtech::description() const
|
||||
{
|
||||
return "VTech (Laser 200/300)";
|
||||
}
|
||||
|
||||
void fs_vtech::enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector<uint32_t> &variants) const
|
||||
{
|
||||
if(has(form_factor, variants, floppy_image::FF_525, floppy_image::SSSD))
|
||||
fe.add(FLOPPY_VTECH_BIN_FORMAT, 163840, "vtech", "VTech");
|
||||
}
|
||||
|
||||
std::unique_ptr<filesystem_t> fs_vtech::mount(fsblk_t &blockdev) const
|
||||
{
|
||||
return std::make_unique<impl>(blockdev);
|
||||
}
|
||||
|
||||
bool fs_vtech::can_format() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs_vtech::can_read() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs_vtech::can_write() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs_vtech::has_rsrc() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<fs_meta_description> fs_vtech::volume_meta_description() const
|
||||
{
|
||||
std::vector<fs_meta_description> res;
|
||||
return res;
|
||||
}
|
||||
|
||||
fs_meta_data fs_vtech::impl::metadata()
|
||||
{
|
||||
fs_meta_data res;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<fs_meta_description> fs_vtech::file_meta_description() const
|
||||
{
|
||||
std::vector<fs_meta_description> res;
|
||||
res.emplace_back(fs_meta_description(fs_meta_name::name, fs_meta_type::string, "", false, [](const fs_meta &m) { return m.as_string().size() <= 8; }, "File name, 8 chars"));
|
||||
res.emplace_back(fs_meta_description(fs_meta_name::loading_address, fs_meta_type::number, 0x7ae9, false, [](const fs_meta &m) { return m.as_number() < 0x10000; }, "Loading address of the file"));
|
||||
res.emplace_back(fs_meta_description(fs_meta_name::length, fs_meta_type::number, 0, true, nullptr, "Size of the file in bytes"));
|
||||
res.emplace_back(fs_meta_description(fs_meta_name::basic, fs_meta_type::flag, true, true, nullptr, "Basic file"));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void fs_vtech::impl::format(const fs_meta_data &meta)
|
||||
{
|
||||
m_blockdev.fill(0);
|
||||
}
|
||||
|
||||
fs_vtech::impl::impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128), m_root(true)
|
||||
{
|
||||
}
|
||||
|
||||
filesystem_t::dir_t fs_vtech::impl::root()
|
||||
{
|
||||
if(!m_root)
|
||||
m_root = new root_dir(*this);
|
||||
return m_root.strong();
|
||||
}
|
||||
|
||||
void fs_vtech::impl::drop_root_ref()
|
||||
{
|
||||
m_root = nullptr;
|
||||
}
|
||||
|
||||
void fs_vtech::impl::root_dir::drop_weak_references()
|
||||
{
|
||||
m_fs.drop_root_ref();
|
||||
}
|
||||
|
||||
fs_meta_data fs_vtech::impl::root_dir::metadata()
|
||||
{
|
||||
return fs_meta_data();
|
||||
}
|
||||
|
||||
std::vector<fs_dir_entry> fs_vtech::impl::root_dir::contents()
|
||||
{
|
||||
std::vector<fs_dir_entry> res;
|
||||
|
||||
uint64_t id = 0;
|
||||
for(int sect = 0; sect != 14; sect++) {
|
||||
auto bdir = m_fs.m_blockdev.get(sect);
|
||||
for(u32 i = 0; i != 8; i ++) {
|
||||
u32 off = i*16;
|
||||
u8 type = bdir.r8(off);
|
||||
if(type != 'T' && type != 'B')
|
||||
continue;
|
||||
if(bdir.r8(off+1) != ':')
|
||||
continue;
|
||||
std::string fname = trim_end_spaces(bdir.rstr(off+2, 8));
|
||||
res.emplace_back(fs_dir_entry(fname, fs_dir_entry_type::file, id));
|
||||
id++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
filesystem_t::file_t fs_vtech::impl::root_dir::file_get(uint64_t key)
|
||||
{
|
||||
if(key >= 15*8)
|
||||
fatalerror("Key out of range\n");
|
||||
|
||||
auto bdir = m_fs.m_blockdev.get(key >> 3);
|
||||
int off = (key & 7) << 4;
|
||||
return file_t(new file(m_fs, this, bdir.rodata() + off, key));
|
||||
}
|
||||
|
||||
void fs_vtech::impl::root_dir::update_file(u16 key, const u8 *entry)
|
||||
{
|
||||
auto bdir = m_fs.m_blockdev.get(key >> 3);
|
||||
int off = (key & 7) << 4;
|
||||
bdir.copy(off, entry, 16);
|
||||
}
|
||||
|
||||
filesystem_t::dir_t fs_vtech::impl::root_dir::dir_get(uint64_t key)
|
||||
{
|
||||
fatalerror("Directories not supported\n");
|
||||
}
|
||||
|
||||
fs_vtech::impl::file::file(impl &fs, root_dir *dir, const u8 *entry, u16 key) : m_fs(fs), m_dir(dir), m_key(key)
|
||||
{
|
||||
memcpy(m_entry, entry, 16);
|
||||
}
|
||||
|
||||
void fs_vtech::impl::file::drop_weak_references()
|
||||
{
|
||||
}
|
||||
|
||||
fs_meta_data fs_vtech::impl::file::metadata()
|
||||
{
|
||||
fs_meta_data res;
|
||||
|
||||
res.set(fs_meta_name::name, trim_end_spaces(rstr(m_entry+2, 8)));
|
||||
res.set(fs_meta_name::basic, m_entry[0] == 'T');
|
||||
res.set(fs_meta_name::loading_address, r16l(m_entry + 0xc));
|
||||
res.set(fs_meta_name::length, ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + 1) & 0xffff));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<u8> fs_vtech::impl::file::read_all()
|
||||
{
|
||||
u8 track = m_entry[0xa];
|
||||
u8 sector = m_entry[0xb];
|
||||
int len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc)) & 0xffff) + 1;
|
||||
|
||||
std::vector<u8> data(len, 0);
|
||||
int pos = 0;
|
||||
while(pos < len) {
|
||||
if(track >= 40 || sector >= 16)
|
||||
break;
|
||||
auto dblk = m_fs.m_blockdev.get(track*16 + sector);
|
||||
int size = len - pos;
|
||||
if(size > 126)
|
||||
size = 126;
|
||||
memcpy(data.data() + pos, dblk.data(), size);
|
||||
pos += size;
|
||||
track = dblk.r8(126);
|
||||
sector = dblk.r8(127);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
fs_vtech::impl::file_t fs_vtech::impl::root_dir::file_create(const fs_meta_data &info)
|
||||
{
|
||||
// Find the key for the next unused entry
|
||||
for(int sect = 0; sect != 14; sect++) {
|
||||
auto bdir = m_fs.m_blockdev.get(sect);
|
||||
uint64_t id = 0;
|
||||
for(u32 i = 0; i != 16; i ++) {
|
||||
u32 off = i*16;
|
||||
u8 type = bdir.r8(off);
|
||||
if(type != 'T' && type != 'B') {
|
||||
std::string fname = info.get_string(fs_meta_name::name, "");
|
||||
fname.resize(8, ' ');
|
||||
|
||||
bdir.w8 (off+0x0, info.get_flag(fs_meta_name::basic, true) ? 'T' : 'B');
|
||||
bdir.w8 (off+0x1, ':');
|
||||
bdir.wstr(off+0x2, fname);
|
||||
bdir.w8 (off+0xa, 0x00);
|
||||
bdir.w8 (off+0xb, 0x00);
|
||||
bdir.w16l(off+0xc, info.get_number(fs_meta_name::loading_address, 0x7ae9));
|
||||
bdir.w16l(off+0xe, bdir.r16l(off+0xc) - 1); // Size 0 initially
|
||||
return file_t(new file(m_fs, this, bdir.rodata() + off, id));
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void fs_vtech::impl::root_dir::file_delete(uint64_t key)
|
||||
{
|
||||
}
|
||||
|
||||
void fs_vtech::impl::file::replace(const std::vector<u8> &data)
|
||||
{
|
||||
u32 cur_len = ((r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + 1) & 0xffff);
|
||||
u32 new_len = data.size();
|
||||
if(new_len > 65535)
|
||||
new_len = 65535;
|
||||
u32 cur_ns = (cur_len + 125)/126;
|
||||
u32 need_ns = (new_len + 125) / 126;
|
||||
|
||||
// Enough space?
|
||||
if(cur_ns < need_ns && m_fs.free_block_count() < need_ns - cur_ns)
|
||||
return;
|
||||
|
||||
u8 track = m_entry[0xa];
|
||||
u8 sector = m_entry[0xb];
|
||||
std::vector<std::pair<u8, u8>> tofree;
|
||||
for(u32 i = 0; i != cur_ns; i++) {
|
||||
tofree.emplace_back(std::make_pair(track, sector));
|
||||
auto dblk = m_fs.m_blockdev.get(track*16 + sector);
|
||||
track = dblk.r8(126);
|
||||
sector = dblk.r8(127);
|
||||
}
|
||||
|
||||
m_fs.free_blocks(tofree);
|
||||
|
||||
std::vector<std::pair<u8, u8>> blocks = m_fs.allocate_blocks(need_ns);
|
||||
for(u32 i=0; i != need_ns; i ++) {
|
||||
auto dblk = m_fs.m_blockdev.get(blocks[i].first * 16 + blocks[i].second);
|
||||
u32 len = new_len - i*126;
|
||||
if(len > 126)
|
||||
len = 126;
|
||||
else if(len < 126)
|
||||
dblk.fill(0x00);
|
||||
memcpy(dblk.data(), data.data() + 126*i, len);
|
||||
if(i < need_ns) {
|
||||
dblk.w8(126, blocks[i+1].first);
|
||||
dblk.w8(127, blocks[i+1].second);
|
||||
} else
|
||||
dblk.w16l(126, 0);
|
||||
}
|
||||
|
||||
u16 end_address = (r16l(m_entry + 0xc) + data.size() - 1) & 0xffff;
|
||||
w16l(m_entry + 0xe, end_address);
|
||||
if(need_ns) {
|
||||
w8(m_entry + 0xa, blocks[0].first);
|
||||
w8(m_entry + 0xb, blocks[0].second);
|
||||
} else
|
||||
w16l(m_entry + 0xa, 0);
|
||||
|
||||
m_dir->update_file(m_key, m_entry);
|
||||
}
|
||||
|
||||
void fs_vtech::impl::root_dir::metadata_change(const fs_meta_data &info)
|
||||
{
|
||||
}
|
||||
|
||||
void fs_vtech::impl::metadata_change(const fs_meta_data &info)
|
||||
{
|
||||
}
|
||||
|
||||
void fs_vtech::impl::file::metadata_change(const fs_meta_data &info)
|
||||
{
|
||||
if(info.has(fs_meta_name::basic))
|
||||
w8 (m_entry+0x0, info.get_flag(fs_meta_name::basic) ? 'T' : 'B');
|
||||
if(info.has(fs_meta_name::name)) {
|
||||
std::string name = info.get_string(fs_meta_name::name);
|
||||
name.resize(8, ' ');
|
||||
wstr(m_entry+0x2, name);
|
||||
}
|
||||
if(info.has(fs_meta_name::loading_address)) {
|
||||
u16 new_loading = info.get_number(fs_meta_name::loading_address);
|
||||
u16 new_end = r16l(m_entry + 0xe) - r16l(m_entry + 0xc) + new_loading;
|
||||
w16l(m_entry + 0xc, new_loading);
|
||||
w16l(m_entry + 0xe, new_end);
|
||||
}
|
||||
m_dir->update_file(m_key, m_entry);
|
||||
}
|
||||
|
||||
std::vector<std::pair<u8, u8>> fs_vtech::impl::allocate_blocks(u32 count)
|
||||
{
|
||||
std::vector<std::pair<u8, u8>> blocks;
|
||||
if(free_block_count() < count)
|
||||
return blocks;
|
||||
|
||||
auto fmap = m_blockdev.get(15);
|
||||
for(u8 track = 1; track != 40; track++)
|
||||
for(u8 sector = 0; sector != 16; sector++) {
|
||||
u32 off = (track-1)*2 + (sector / 8);
|
||||
u32 bit = 1 << (sector & 7);
|
||||
if(!(fmap.r8(off) & bit)) {
|
||||
fmap.w8(off, fmap.r8(off) | bit);
|
||||
blocks.emplace_back(std::make_pair(track, sector));
|
||||
if(blocks.size() == count)
|
||||
return blocks;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
void fs_vtech::impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks)
|
||||
{
|
||||
auto fmap = m_blockdev.get(15);
|
||||
for(auto ref : blocks) {
|
||||
u8 track = ref.first;
|
||||
u8 sector = ref.second;
|
||||
u32 off = (track-1)*2 + (sector / 8);
|
||||
u32 bit = 1 << (sector & 7);
|
||||
fmap.w8(off, fmap.r8(off) & ~bit);
|
||||
}
|
||||
}
|
||||
|
||||
u32 fs_vtech::impl::free_block_count()
|
||||
{
|
||||
auto fmap = m_blockdev.get(15);
|
||||
u32 nf = 0;
|
||||
for(u32 off = 0; off != (40-1)*2; off++) {
|
||||
u8 m = fmap.r8(off);
|
||||
// Count 1 bits;
|
||||
m = ((m & 0xaa) >> 1) | (m & 0x55);
|
||||
m = ((m & 0xcc) >> 2) | (m & 0x33);
|
||||
m = ((m & 0xf0) >> 4) | (m & 0x0f);
|
||||
nf += 7-m;
|
||||
}
|
||||
return nf;
|
||||
}
|
96
src/lib/formats/fs_vtech.h
Normal file
96
src/lib/formats/fs_vtech.h
Normal file
@ -0,0 +1,96 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
|
||||
// Management of VTech images
|
||||
|
||||
#ifndef MAME_FORMATS_FS_VTECH_H
|
||||
#define MAME_FORMATS_FS_VTECH_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fsmgr.h"
|
||||
|
||||
class fs_vtech : public filesystem_manager_t {
|
||||
public:
|
||||
class impl : public filesystem_t {
|
||||
public:
|
||||
class root_dir : public idir_t {
|
||||
public:
|
||||
root_dir(impl &i) : m_fs(i) {}
|
||||
virtual ~root_dir() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual fs_meta_data metadata() override;
|
||||
virtual void metadata_change(const fs_meta_data &info) override;
|
||||
virtual std::vector<fs_dir_entry> contents() override;
|
||||
virtual file_t file_get(uint64_t key) override;
|
||||
virtual dir_t dir_get(uint64_t key) override;
|
||||
virtual file_t file_create(const fs_meta_data &info) override;
|
||||
virtual void file_delete(uint64_t key) override;
|
||||
|
||||
void update_file(u16 key, const u8 *entry);
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
|
||||
std::pair<fsblk_t::block_t, u32> get_dir_block(uint64_t key);
|
||||
};
|
||||
|
||||
class file : public ifile_t {
|
||||
public:
|
||||
file(impl &fs, root_dir *dir, const u8 *entry, u16 key);
|
||||
virtual ~file() = default;
|
||||
|
||||
virtual void drop_weak_references() override;
|
||||
|
||||
virtual fs_meta_data metadata() override;
|
||||
virtual void metadata_change(const fs_meta_data &info) override;
|
||||
virtual std::vector<u8> read_all() override;
|
||||
virtual void replace(const std::vector<u8> &data) override;
|
||||
|
||||
private:
|
||||
impl &m_fs;
|
||||
root_dir *m_dir;
|
||||
u16 m_key;
|
||||
u8 m_entry[18];
|
||||
};
|
||||
|
||||
impl(fsblk_t &blockdev);
|
||||
virtual ~impl() = default;
|
||||
|
||||
virtual void format(const fs_meta_data &meta) override;
|
||||
virtual fs_meta_data metadata() override;
|
||||
virtual void metadata_change(const fs_meta_data &info) override;
|
||||
virtual dir_t root() override;
|
||||
|
||||
void drop_root_ref();
|
||||
|
||||
std::vector<std::pair<u8, u8>> allocate_blocks(u32 count);
|
||||
void free_blocks(const std::vector<std::pair<u8, u8>> &blocks);
|
||||
u32 free_block_count();
|
||||
|
||||
private:
|
||||
dir_t m_root;
|
||||
};
|
||||
|
||||
fs_vtech() : filesystem_manager_t() {}
|
||||
|
||||
virtual const char *name() const override;
|
||||
virtual const char *description() const override;
|
||||
|
||||
virtual void enumerate_f(floppy_enumerator &fe, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
|
||||
virtual std::unique_ptr<filesystem_t> 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<fs_meta_description> volume_meta_description() const override;
|
||||
virtual std::vector<fs_meta_description> file_meta_description() const override;
|
||||
};
|
||||
|
||||
extern const fs_vtech FS_VTECH;
|
||||
|
||||
#endif
|
@ -369,6 +369,14 @@ uint32_t filesystem_t::r32l(const uint8_t *p)
|
||||
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||||
}
|
||||
|
||||
std::string filesystem_t::trim_end_spaces(const std::string &str)
|
||||
{
|
||||
auto i = str.end();
|
||||
while(i != str.begin() && i[-1] == ' ')
|
||||
i--;
|
||||
return std::string(str.begin(), i);
|
||||
}
|
||||
|
||||
filesystem_t::file_t filesystem_t::idir_t::file_create(const fs_meta_data &info)
|
||||
{
|
||||
fatalerror("file_create called on a filesystem not supporting write\n");
|
||||
@ -413,6 +421,7 @@ std::vector<u8> filesystem_t::ifile_t::rsrc_read_all()
|
||||
const char *fs_meta_data::entry_name(fs_meta_name name)
|
||||
{
|
||||
switch(name) {
|
||||
case fs_meta_name::basic: return "basic";
|
||||
case fs_meta_name::creation_date: return "creation_date";
|
||||
case fs_meta_name::length: return "length";
|
||||
case fs_meta_name::loading_address: return "loading_address";
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <functional>
|
||||
|
||||
enum class fs_meta_name {
|
||||
basic,
|
||||
creation_date,
|
||||
length,
|
||||
loading_address,
|
||||
@ -380,6 +381,8 @@ public:
|
||||
static uint32_t r24l(const uint8_t *p);
|
||||
static uint32_t r32l(const uint8_t *p);
|
||||
|
||||
static std::string trim_end_spaces(const std::string &str);
|
||||
|
||||
protected:
|
||||
fsblk_t &m_blockdev;
|
||||
};
|
||||
|
@ -146,8 +146,10 @@ bool mfi_format::load(io_generic *io, uint32_t form_factor, const std::vector<ui
|
||||
trackbuf.resize(cell_count);
|
||||
|
||||
uLongf size = ent->uncompressed_size;
|
||||
if(uncompress((Bytef *)&trackbuf[0], &size, &compressed[0], ent->compressed_size) != Z_OK)
|
||||
if(uncompress((Bytef *)&trackbuf[0], &size, &compressed[0], ent->compressed_size) != Z_OK) {
|
||||
fprintf(stderr, "fail1\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t cur_time = 0;
|
||||
for(unsigned int i=0; i != cell_count; i++) {
|
||||
@ -155,12 +157,15 @@ bool mfi_format::load(io_generic *io, uint32_t form_factor, const std::vector<ui
|
||||
trackbuf[i] = (trackbuf[i] & MG_MASK) | cur_time;
|
||||
cur_time = next_cur_time;
|
||||
}
|
||||
if(cur_time != 200000000)
|
||||
if(cur_time != 200000000) {
|
||||
fprintf(stderr, "fail2 %d\n", cur_time);
|
||||
return false;
|
||||
}
|
||||
|
||||
ent++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "tick\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
396
src/lib/formats/vt_dsk.cpp
Normal file
396
src/lib/formats/vt_dsk.cpp
Normal file
@ -0,0 +1,396 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
/*********************************************************************
|
||||
|
||||
formats/vt_dsk.cpp
|
||||
|
||||
VTech disk image formats
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#include "formats/vt_dsk.h"
|
||||
|
||||
// Zero = | 9187 |
|
||||
// One = | 2237 | 6950 |
|
||||
// 0.5us ~= 143
|
||||
|
||||
void vtech_common_format::wbit(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, bool bit)
|
||||
{
|
||||
buffer.push_back(pos | mg);
|
||||
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
|
||||
if(bit) {
|
||||
pos += 2237;
|
||||
buffer.push_back(pos | mg);
|
||||
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
|
||||
pos += 6950;
|
||||
} else
|
||||
pos += 9187;
|
||||
}
|
||||
|
||||
void vtech_common_format::wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, uint8_t byte)
|
||||
{
|
||||
for(int i = 7; i >= 0; i--)
|
||||
wbit(buffer, pos, mg, (byte >> i) & 1);
|
||||
}
|
||||
|
||||
void vtech_common_format::image_to_flux(const std::vector<uint8_t> &bdata, floppy_image *image)
|
||||
{
|
||||
static const uint8_t sector_map[16] = {
|
||||
0x0, 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8, 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5
|
||||
};
|
||||
|
||||
for(int track = 0; track != 40; track ++) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t mg = floppy_image::MG_A;
|
||||
std::vector<uint32_t> &buffer = image->get_buffer(track, 0);
|
||||
buffer.clear();
|
||||
image->set_write_splice_position(track, 0, 0);
|
||||
// One window of pad at the start to avoid problems with the write splice
|
||||
wbit(buffer, pos, mg, 0);
|
||||
|
||||
for(int sector = 0; sector != 16; sector ++) {
|
||||
uint8_t sid = sector_map[sector];
|
||||
for(int i=0; i != 7; i++)
|
||||
wbyte(buffer, pos, mg, 0x80);
|
||||
wbyte(buffer, pos, mg, 0x00);
|
||||
wbyte(buffer, pos, mg, 0xfe);
|
||||
wbyte(buffer, pos, mg, 0xe7);
|
||||
wbyte(buffer, pos, mg, 0x18);
|
||||
wbyte(buffer, pos, mg, 0xc3);
|
||||
wbyte(buffer, pos, mg, track);
|
||||
wbyte(buffer, pos, mg, sid);
|
||||
wbyte(buffer, pos, mg, track+sid);
|
||||
for(int i=0; i != 5; i++)
|
||||
wbyte(buffer, pos, mg, 0x80);
|
||||
wbyte(buffer, pos, mg, 0x00);
|
||||
wbyte(buffer, pos, mg, 0xc3);
|
||||
wbyte(buffer, pos, mg, 0x18);
|
||||
wbyte(buffer, pos, mg, 0xe7);
|
||||
wbyte(buffer, pos, mg, 0xfe);
|
||||
uint16_t chk = 0;
|
||||
const uint8_t *src = bdata.data() + 16*128*track + 128*sid;
|
||||
for(int i=0; i != 128; i++) {
|
||||
chk += src[i];
|
||||
wbyte(buffer, pos, mg, src[i]);
|
||||
}
|
||||
wbyte(buffer, pos, mg, chk);
|
||||
wbyte(buffer, pos, mg, chk >> 8);
|
||||
}
|
||||
// Rest is just not formatted
|
||||
buffer.push_back(pos | floppy_image::MG_N);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> vtech_common_format::flux_to_image(floppy_image *image)
|
||||
{
|
||||
std::vector<uint8_t> bdata(16*256*40, 0);
|
||||
|
||||
for(int track = 0; track != 40; track++) {
|
||||
auto buffer = image->get_buffer(track, 0);
|
||||
int sz = buffer.size();
|
||||
if(sz < 128)
|
||||
continue;
|
||||
|
||||
std::vector<bool> bitstream;
|
||||
int lpos = -1;
|
||||
bool looped = !((buffer[sz-1] ^ buffer[0]) & floppy_image::MG_MASK);
|
||||
int cpos = looped ? sz-1 : 0;
|
||||
while(cpos != lpos) {
|
||||
int dt = looped && cpos == sz-1 ? (200000000 - (buffer[cpos] & floppy_image::TIME_MASK)) + (buffer[1] & floppy_image::TIME_MASK) :
|
||||
cpos == sz-1 ? 200000000 - (buffer[cpos] & floppy_image::TIME_MASK) :
|
||||
(buffer[cpos+1] & floppy_image::TIME_MASK) - (buffer[cpos] & floppy_image::TIME_MASK);
|
||||
int t = dt >= 9187 - 143 ? 0 :
|
||||
dt >= 2237 - 143 && dt <= 2237 + 143 ? 1 :
|
||||
2;
|
||||
if(t <= 1) {
|
||||
if(lpos == -1)
|
||||
lpos = cpos;
|
||||
bitstream.push_back(t);
|
||||
}
|
||||
cpos += 1;
|
||||
if(cpos == sz)
|
||||
cpos = looped ? 1 : 0;
|
||||
}
|
||||
int mode = 0;
|
||||
looped = false;
|
||||
int pos = 0;
|
||||
int count = 0;
|
||||
uint8_t *dest = nullptr;
|
||||
uint16_t checksum = 0;
|
||||
uint64_t buf = 0;
|
||||
sz = bitstream.size();
|
||||
if(sz < 128)
|
||||
continue;
|
||||
|
||||
for(int i=0; i != 63; i++)
|
||||
buf = (buf << 1) | bitstream[sz-64+i];
|
||||
for(;;) {
|
||||
buf = (buf << 1) | bitstream[pos];
|
||||
count ++;
|
||||
switch(mode) {
|
||||
case 0: // idle
|
||||
if(buf == 0x80808000fee718c3)
|
||||
mode = 1;
|
||||
count = 0;
|
||||
break;
|
||||
|
||||
case 1: // sector header
|
||||
if(count == 24) {
|
||||
uint8_t trk = buf >> 16;
|
||||
uint8_t sector = buf >> 8;
|
||||
uint8_t chk = buf;
|
||||
if(chk != sector + trk) {
|
||||
mode = 0;
|
||||
break;
|
||||
}
|
||||
checksum = 0;
|
||||
dest = bdata.data() + 128 * 16 * trk + 128 * (sector & 0xf);
|
||||
mode = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // look for sector data
|
||||
if(buf == 0x80808000fee718c3)
|
||||
mode = 1;
|
||||
else if(buf == 0x80808000c318e7fe)
|
||||
mode = 3;
|
||||
count = 0;
|
||||
break;
|
||||
|
||||
case 3: // sector data
|
||||
if(count <= 128*8 && !(count & 7)) {
|
||||
uint8_t byte = buf;
|
||||
checksum += byte;
|
||||
*dest++ = byte;
|
||||
} else if(count == 128*8+16) {
|
||||
// uint16_t disk_checksum = buf;
|
||||
// printf("sector checksum %04x %04x\n", checksum, disk_checksum);
|
||||
mode = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(mode == 0 && looped)
|
||||
break;
|
||||
pos++;
|
||||
if(pos == sz) {
|
||||
pos = 0;
|
||||
looped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bdata;
|
||||
}
|
||||
|
||||
|
||||
const char *vtech_bin_format::name() const
|
||||
{
|
||||
return "vtech_bin";
|
||||
}
|
||||
|
||||
const char *vtech_bin_format::description() const
|
||||
{
|
||||
return "VTech sector disk image";
|
||||
}
|
||||
|
||||
const char *vtech_bin_format::extensions() const
|
||||
{
|
||||
return "bin";
|
||||
}
|
||||
|
||||
const char *vtech_dsk_format::name() const
|
||||
{
|
||||
return "vtech_dsk";
|
||||
}
|
||||
|
||||
const char *vtech_dsk_format::description() const
|
||||
{
|
||||
return "VTech dsk image";
|
||||
}
|
||||
|
||||
const char *vtech_dsk_format::extensions() const
|
||||
{
|
||||
return "dsk";
|
||||
}
|
||||
|
||||
int vtech_bin_format::identify(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants)
|
||||
{
|
||||
int size = io_generic_size(io);
|
||||
|
||||
if(size == 40*16*256)
|
||||
return 50;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vtech_dsk_format::identify(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants)
|
||||
{
|
||||
int size = io_generic_size(io);
|
||||
if(size < 256)
|
||||
return 0;
|
||||
|
||||
std::vector<uint8_t> bdata(size);
|
||||
io_generic_read(io, bdata.data(), 0, size);
|
||||
|
||||
// Structurally validate the presence of sector headers and data
|
||||
int count_sh = 0, count_sd = 0;
|
||||
uint64_t buf = 0;
|
||||
for(uint8_t b : bdata) {
|
||||
buf = (buf << 8) | b;
|
||||
if(buf == 0x80808000fee718c3)
|
||||
count_sh++;
|
||||
else if(buf == 0x80808000c318e7fe)
|
||||
count_sd++;
|
||||
}
|
||||
|
||||
return count_sh >= 30*16 && count_sd >= 30*16 ? 100 : 0;
|
||||
}
|
||||
|
||||
bool vtech_bin_format::load(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image)
|
||||
{
|
||||
int size = io_generic_size(io);
|
||||
if(size != 40*16*256)
|
||||
return false;
|
||||
|
||||
std::vector<uint8_t> bdata(size);
|
||||
io_generic_read(io, bdata.data(), 0, size);
|
||||
|
||||
image_to_flux(bdata, image);
|
||||
image->set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vtech_dsk_format::load(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image)
|
||||
{
|
||||
int size = io_generic_size(io);
|
||||
std::vector<uint8_t> bdata(size);
|
||||
io_generic_read(io, bdata.data(), 0, size);
|
||||
|
||||
std::vector<uint8_t> bdatax(128*16*40, 0);
|
||||
|
||||
int mode = 0;
|
||||
int count = 0;
|
||||
uint16_t checksum = 0;
|
||||
uint64_t buf = 0;
|
||||
uint8_t *dest = nullptr;
|
||||
|
||||
for(uint8_t b : bdata) {
|
||||
buf = (buf << 8) | b;
|
||||
count ++;
|
||||
switch(mode) {
|
||||
case 0: // idle
|
||||
if(buf == 0x80808000fee718c3)
|
||||
mode = 1;
|
||||
count = 0;
|
||||
break;
|
||||
|
||||
case 1: // sector header
|
||||
if(count == 3) {
|
||||
uint8_t trk = buf >> 16;
|
||||
uint8_t sector = buf >> 8;
|
||||
uint8_t chk = buf;
|
||||
if(chk != sector + trk) {
|
||||
mode = 0;
|
||||
break;
|
||||
}
|
||||
dest = bdatax.data() + 128*16*trk + sector*128;
|
||||
checksum = 0;
|
||||
mode = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // look for sector data
|
||||
if(buf == 0x80808000fee718c3)
|
||||
mode = 1;
|
||||
else if(buf == 0x80808000c318e7fe)
|
||||
mode = 3;
|
||||
count = 0;
|
||||
break;
|
||||
|
||||
case 3: // sector data
|
||||
if(count <= 128) {
|
||||
uint8_t byte = buf;
|
||||
checksum += byte;
|
||||
*dest++ = byte;
|
||||
|
||||
} else if(count == 128+2) {
|
||||
uint16_t disk_checksum = buf;
|
||||
disk_checksum = (disk_checksum << 8) | (disk_checksum >> 8);
|
||||
mode = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
image_to_flux(bdatax, image);
|
||||
image->set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vtech_bin_format::save(io_generic *io, const std::vector<uint32_t> &variants, floppy_image *image)
|
||||
{
|
||||
int tracks, heads;
|
||||
image->get_maximal_geometry(tracks, heads);
|
||||
if(tracks < 40)
|
||||
return false;
|
||||
|
||||
auto bdata = flux_to_image(image);
|
||||
io_generic_write(io, bdata.data(), 0, bdata.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vtech_dsk_format::save(io_generic *io, const std::vector<uint32_t> &variants, floppy_image *image)
|
||||
{
|
||||
int tracks, heads;
|
||||
image->get_maximal_geometry(tracks, heads);
|
||||
if(tracks < 40)
|
||||
return false;
|
||||
|
||||
auto bdata = flux_to_image(image);
|
||||
std::vector<uint8_t> bdatax(0x9b*16*40);
|
||||
|
||||
// Format is essentially an idealized version of what's written on the disk
|
||||
|
||||
static const uint8_t sector_map[16] = {
|
||||
0x0, 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8, 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5
|
||||
};
|
||||
|
||||
int pos = 0;
|
||||
for(int track = 0; track != 40; track ++) {
|
||||
for(int sector = 0; sector != 16; sector ++) {
|
||||
uint8_t sid = sector_map[sector];
|
||||
for(int i=0; i != 7; i++)
|
||||
bdatax[pos++] = 0x80;
|
||||
bdatax[pos++] = 0x00;
|
||||
bdatax[pos++] = 0xfe;
|
||||
bdatax[pos++] = 0xe7;
|
||||
bdatax[pos++] = 0x18;
|
||||
bdatax[pos++] = 0xc3;
|
||||
bdatax[pos++] = track;
|
||||
bdatax[pos++] = sid;
|
||||
bdatax[pos++] = track+sid;
|
||||
for(int i=0; i != 5; i++)
|
||||
bdatax[pos++] = 0x80;
|
||||
bdatax[pos++] = 0x00;
|
||||
bdatax[pos++] = 0xc3;
|
||||
bdatax[pos++] = 0x18;
|
||||
bdatax[pos++] = 0xe7;
|
||||
bdatax[pos++] = 0xfe;
|
||||
uint16_t chk = 0;
|
||||
const uint8_t *src = bdata.data() + 16*128*track + 128*sid;
|
||||
for(int i=0; i != 128; i++) {
|
||||
chk += src[i];
|
||||
bdatax[pos++] = src[i];
|
||||
}
|
||||
bdatax[pos++] = chk;
|
||||
bdatax[pos++] = chk >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
io_generic_write(io, bdatax.data(), 0, bdatax.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const floppy_format_type FLOPPY_VTECH_BIN_FORMAT = &floppy_image_format_creator<vtech_bin_format>;
|
||||
const floppy_format_type FLOPPY_VTECH_DSK_FORMAT = &floppy_image_format_creator<vtech_dsk_format>;
|
58
src/lib/formats/vt_dsk.h
Normal file
58
src/lib/formats/vt_dsk.h
Normal file
@ -0,0 +1,58 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
/*********************************************************************
|
||||
|
||||
formats/vt_dsk.h
|
||||
|
||||
VTech disk image formats
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef MAME_FORMATS_VT_DSK_H
|
||||
#define MAME_FORMATS_VT_DSK_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "flopimg.h"
|
||||
|
||||
class vtech_common_format : public floppy_image_format_t {
|
||||
public:
|
||||
virtual bool supports_save() const override { return true; }
|
||||
|
||||
protected:
|
||||
static void image_to_flux(const std::vector<uint8_t> &bdata, floppy_image *image);
|
||||
static std::vector<uint8_t> flux_to_image(floppy_image *image);
|
||||
|
||||
static void wbit(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, bool bit);
|
||||
static void wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, uint8_t byte);
|
||||
};
|
||||
|
||||
class vtech_bin_format : public vtech_common_format {
|
||||
public:
|
||||
vtech_bin_format() = default;
|
||||
|
||||
virtual const char *name() const override;
|
||||
virtual const char *description() const override;
|
||||
virtual const char *extensions() const override;
|
||||
|
||||
virtual int identify(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants) override;
|
||||
virtual bool load(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image) override;
|
||||
virtual bool save(io_generic *io, const std::vector<uint32_t> &variants, floppy_image *image) override;
|
||||
};
|
||||
|
||||
class vtech_dsk_format : public vtech_common_format {
|
||||
public:
|
||||
vtech_dsk_format() = default;
|
||||
|
||||
virtual const char *name() const override;
|
||||
virtual const char *description() const override;
|
||||
virtual const char *extensions() const override;
|
||||
|
||||
virtual int identify(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants) override;
|
||||
virtual bool load(io_generic *io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image) override;
|
||||
virtual bool save(io_generic *io, const std::vector<uint32_t> &variants, floppy_image *image) override;
|
||||
};
|
||||
|
||||
extern const floppy_format_type FLOPPY_VTECH_BIN_FORMAT;
|
||||
extern const floppy_format_type FLOPPY_VTECH_DSK_FORMAT;
|
||||
|
||||
#endif // MAME_FORMATS_VT_DSK_H
|
Loading…
Reference in New Issue
Block a user