-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #668 from davidgiven/ms2000
Add basic support for the MS2000 Microdos file system.
- Loading branch information
Showing
10 changed files
with
311 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
#include "lib/globals.h" | ||
#include "lib/vfs/vfs.h" | ||
#include "lib/config.pb.h" | ||
#include "lib/utils.h" | ||
#include <fmt/format.h> | ||
|
||
/* See https://www.hp9845.net/9845/projects/hpdir/#lif_filesystem for | ||
* a description. */ | ||
|
||
static void trimZeros(std::string s) | ||
{ | ||
s.erase(std::remove(s.begin(), s.end(), 0), s.end()); | ||
} | ||
|
||
class MicrodosFilesystem : public Filesystem | ||
{ | ||
struct SDW | ||
{ | ||
unsigned start; | ||
unsigned length; | ||
}; | ||
|
||
class MicrodosDirent : public Dirent | ||
{ | ||
public: | ||
MicrodosDirent(MicrodosFilesystem& fs, Bytes& bytes) | ||
{ | ||
file_type = TYPE_FILE; | ||
|
||
ByteReader br(bytes); | ||
auto stem = trimWhitespace(br.read(6)); | ||
auto ext = trimWhitespace(br.read(3)); | ||
filename = fmt::format("{}.{}", stem, ext); | ||
|
||
br.skip(1); | ||
ssn = br.read_be16(); | ||
attr = br.read_8(); | ||
|
||
Bytes rib = fs.getLogicalSector(ssn); | ||
ByteReader rbr(rib); | ||
for (int i = 0; i < 57; i++) | ||
{ | ||
unsigned w = rbr.read_be16(); | ||
if (w & 0x8000) | ||
{ | ||
/* Last. */ | ||
sectors = w & 0x7fff; | ||
break; | ||
} | ||
else | ||
{ | ||
/* Each record except the last is 24 bits long. */ | ||
w = (w << 8) | rbr.read_8(); | ||
sdws.emplace_back(SDW{w & 0xffff, (w >> 16) + 1}); | ||
} | ||
} | ||
rbr.seek(500); | ||
lastSectorBytes = rbr.read_be16(); | ||
loadSectors = rbr.read_be16(); | ||
loadAddress = rbr.read_be16(); | ||
startAddress = rbr.read_be16(); | ||
|
||
length = sectors * 512; | ||
|
||
mode = ""; | ||
path = {filename}; | ||
|
||
attributes[Filesystem::FILENAME] = filename; | ||
attributes[Filesystem::LENGTH] = std::to_string(length); | ||
attributes[Filesystem::FILE_TYPE] = "file"; | ||
attributes[Filesystem::MODE] = mode; | ||
attributes["microdos.ssn"] = std::to_string(ssn); | ||
attributes["microdos.attr"] = fmt::format("0x{:x}", attr); | ||
attributes["microdos.sdw_count"] = std::to_string(sdws.size()); | ||
attributes["microdos.total_sectors"] = std::to_string(sectors); | ||
attributes["microdos.lastSectorBytes"] = | ||
std::to_string(lastSectorBytes); | ||
attributes["microdos.loadSectors"] = std::to_string(loadSectors); | ||
attributes["microdos.loadAddress"] = | ||
fmt::format("0x{:x}", loadAddress); | ||
attributes["microdos.startAddress"] = | ||
fmt::format("0x{:x}", startAddress); | ||
} | ||
|
||
public: | ||
unsigned ssn; | ||
unsigned attr; | ||
std::vector<SDW> sdws; | ||
unsigned sectors; | ||
unsigned lastSectorBytes; | ||
unsigned loadSectors; | ||
unsigned loadAddress; | ||
unsigned startAddress; | ||
}; | ||
|
||
public: | ||
MicrodosFilesystem( | ||
const MicrodosProto& config, std::shared_ptr<SectorInterface> sectors): | ||
Filesystem(sectors), | ||
_config(config) | ||
{ | ||
} | ||
|
||
uint32_t capabilities() const | ||
{ | ||
return OP_GETFSDATA | OP_LIST | OP_GETFILE | OP_GETDIRENT; | ||
} | ||
|
||
FilesystemStatus check() override | ||
{ | ||
return FS_OK; | ||
} | ||
|
||
std::map<std::string, std::string> getMetadata() override | ||
{ | ||
mount(); | ||
|
||
std::map<std::string, std::string> attributes; | ||
|
||
attributes[VOLUME_NAME] = _volumeLabel; | ||
attributes[TOTAL_BLOCKS] = std::to_string(_totalBlocks); | ||
attributes[USED_BLOCKS] = std::to_string(_usedBlocks); | ||
attributes[BLOCK_SIZE] = "512"; | ||
return attributes; | ||
} | ||
|
||
std::shared_ptr<Dirent> getDirent(const Path& path) override | ||
{ | ||
mount(); | ||
if (path.size() != 1) | ||
throw BadPathException(); | ||
|
||
return findFile(path.front()); | ||
} | ||
|
||
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override | ||
{ | ||
mount(); | ||
if (!path.empty()) | ||
throw FileNotFoundException(); | ||
|
||
std::vector<std::shared_ptr<Dirent>> result; | ||
for (auto& de : _dirents) | ||
result.push_back(de); | ||
return result; | ||
} | ||
|
||
Bytes getFile(const Path& path) override | ||
{ | ||
mount(); | ||
if (path.size() != 1) | ||
throw BadPathException(); | ||
|
||
auto dirent = findFile(path.front()); | ||
|
||
Bytes data; | ||
ByteWriter bw(data); | ||
for (const auto& sdw : dirent->sdws) | ||
bw += getLogicalSector(sdw.start, sdw.length); | ||
|
||
return data.slice(512); | ||
} | ||
|
||
private: | ||
void mount() | ||
{ | ||
_rootBlock = getLogicalSector(0); | ||
_catBlock = getLogicalSector(9); | ||
Bytes directory = getLogicalSector(1, 8); | ||
|
||
ByteReader rbr(_rootBlock); | ||
rbr.seek(20); | ||
_volumeLabel = trimWhitespace(rbr.read(44)); | ||
|
||
_dirents.clear(); | ||
ByteReader dbr(directory); | ||
while (!dbr.eof()) | ||
{ | ||
Bytes direntBytes = dbr.read(16); | ||
if ((direntBytes[0] != 0) && (direntBytes[0] != 0xff)) | ||
{ | ||
auto dirent = | ||
std::make_unique<MicrodosDirent>(*this, direntBytes); | ||
_dirents.push_back(std::move(dirent)); | ||
} | ||
} | ||
|
||
ByteReader cbr(_catBlock); | ||
_totalBlocks = 630; | ||
_usedBlocks = 0; | ||
for (int i = 0; i < _totalBlocks / 8; i++) | ||
{ | ||
uint8_t b = cbr.read_8(); | ||
_usedBlocks += countSetBits(b); | ||
} | ||
} | ||
|
||
std::shared_ptr<MicrodosDirent> findFile(const std::string filename) | ||
{ | ||
for (const auto& dirent : _dirents) | ||
{ | ||
if (dirent->filename == filename) | ||
return dirent; | ||
} | ||
|
||
throw FileNotFoundException(); | ||
} | ||
|
||
private: | ||
const MicrodosProto& _config; | ||
Bytes _rootBlock; | ||
Bytes _catBlock; | ||
std::string _volumeLabel; | ||
unsigned _totalBlocks; | ||
unsigned _usedBlocks; | ||
std::vector<std::shared_ptr<MicrodosDirent>> _dirents; | ||
}; | ||
|
||
std::unique_ptr<Filesystem> Filesystem::createMicrodosFilesystem( | ||
const FilesystemProto& config, std::shared_ptr<SectorInterface> sectors) | ||
{ | ||
return std::make_unique<MicrodosFilesystem>(config.microdos(), sectors); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ FORMATS = \ | |
icl30 \ | ||
mac \ | ||
micropolis \ | ||
ms2000 \ | ||
mx \ | ||
n88basic \ | ||
northstar \ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
comment: 'MS2000 Microdisk Development System' | ||
|
||
documentation: | ||
<<< | ||
The RCA MicroDisk Development System MS2000 is a highly obscure (i.e. I gather | ||
that single digit numbers of original machines exist) development system for the | ||
RCA1802 series of CPUs, as made famous by the Cosmac ELF. It was a fairly | ||
straightforward big bag o'RAM system with a 2kB boot ROM, 62kB of RAM, twin | ||
floppy drives and a serial terminal --- CP/M users will find it very familiar. | ||
|
||
Read and writing disks is currently not supported by FluxEngine, but there is | ||
basic support for the MicroDisk operating system's file system. This should | ||
allow files to be read from MS2000 disk images. | ||
|
||
The disks are normal DD 3.5" disks, using a 70-track, single sided variation of | ||
the venerable IBM floppy disk scheme, so allowing 315kB of storage per disk. | ||
|
||
If you have access to flux files for MS2000 disks, please [get in | ||
touch](https://github.com/davidgiven/cpm65/issues/new) --- I would like to add | ||
better support for these. | ||
>>> | ||
|
||
documentation: | ||
<<< | ||
## References | ||
|
||
- [The EMMA-02 emulator](https://www.emma02.hobby-site.com/ms2000.html), which | ||
supports the MS2000 and provides information on it. | ||
>>> | ||
|
||
image_reader { | ||
filename: "ms2000.img" | ||
type: IMAGETYPE_IMG | ||
} | ||
|
||
image_writer { | ||
filename: "ms2000.img" | ||
type: IMAGETYPE_IMG | ||
} | ||
|
||
layout { | ||
tracks: 70 | ||
sides: 1 | ||
tpi: 135 | ||
layoutdata { | ||
sector_size: 512 | ||
physical { | ||
start_sector: 1 | ||
count: 9 | ||
} | ||
} | ||
} | ||
|
||
filesystem { | ||
type: MICRODOS | ||
} | ||
|
||
|