|
|
nono 1.7.0
//
// nono
// Copyright (C) 2023 nono project
// Licensed under nono-license.txt
//
//
// Windrv (相当)
//
#pragma once
#include "device.h"
#include <array>
#include <deque>
#include <map>
class HostWindrv;
class MainbusDevice;
class MPU680x0Device;
class WindrvDevice;
// Human68k の定数とか構造体とか。
class Human68k
{
public:
static const uint MAX_PATH = 96;
static const uint8 ATTR_RDONLY = 0x01;
static const uint8 ATTR_HIDDEN = 0x02;
static const uint8 ATTR_SYSTEM = 0x04;
static const uint8 ATTR_VOLUME = 0x08;
static const uint8 ATTR_DIR = 0x10;
static const uint8 ATTR_ARCHIVE = 0x20;
// 標準(<fcntl.h>) の O_RDONLY とかは #define なので、
// ここで別の名前空間で同名の定数を定義するとかが出来ない。
static const int OPEN_RDONLY = 0;
static const int OPEN_WRONLY = 1;
static const int OPEN_RDWR = 2;
#define DEF static const uint32
// エラーコード(下位バイト)
DEF ERR_INVALID_UNIT = 0x0001; // 無効なユニット番号
DEF ERR_INVALID_COMMAND = 0x0003; // 無効なコマンド
DEF ERR_WRITE = 0x000a; // 書き込みエラー
DEF ERR_READ = 0x000b; // 読み込みエラー
DEF ERR_MISC = 0x000c; // エラーが発生しました
DEF ERR_WRITE_PROTECT = 0x000e; // 書き込み保護されている
// エラーコード(上位バイト)
DEF ERR_IGNORE = 0x4000; // 無視(I)
DEF ERR_RETRY = 0x2000; // 再実行(R)
DEF ERR_ABORT = 0x1000; // 中止(A)
// 合成したもの
DEF ERR_INVALID_UNIT_IA = ERR_INVALID_UNIT | ERR_IGNORE | ERR_ABORT;
DEF ERR_INVALID_COMMAND_IA = ERR_INVALID_COMMAND | ERR_IGNORE | ERR_ABORT;
// ステータスコード
DEF RES_INVALID_FUNC = -1; // 無効なファンクションコード
DEF RES_FILE_NOT_FOUND = -2; // ファイルが見付からない
DEF RES_DIR_NOT_FOUND = -3; // ディレクトリが見付からない
DEF RES_TOO_MANY_HANDLE = -4; // オープンしているハンドルが多い
DEF RES_NOT_A_FILE = -5; // ディレクトリ・ボリュームは不可
DEF RES_NOT_OPENED = -6; // ハンドルはオープンされていない
DEF RES_INVALID_MEM = -7; // メモリ管理領域が不正
DEF RES_NO_MEMORY = -8; // メモリが足りない
DEF RES_INVALID_PTR = -9; // ポインタが無効
DEF RES_INVALID_ENV = -10; // 環境が不正
DEF RES_ILLEGAL_FORMAT = -11; // 実行ファイルのフォーマットが不正
DEF RES_INVALID_MODE = -12; // オープンのアクセスモードが不正
DEF RES_INVALID_PATH = -13; // ファイル名が不正
DEF RES_INVALID_PARAM = -14; // パラメータが不正
DEF RES_INVALID_DRIVE = -15; // ドライブ指定が不正
DEF RES_CURRENT_DIR = -16; // カレントディレクトリは削除出来ない
DEF RES_NO_IOCTRL = -17; // IOCTRL 出来ないデバイス
DEF RES_LAST_FILE = -18; // これ以上ファイルが見付からない
DEF RES_CANNOT_WRITE = -19; // ファイルは書き込み出来ない
DEF RES_DIR_EXISTS = -20; // ディレクトリはすでに存在する
DEF RES_CANNOT_DELETE = -21; // ファイルがあるので削除出来ない
DEF RES_CANNOT_RENAME = -22; // ファイルがあるのでリネーム出来ない
DEF RES_DISK_FULL = -23; // ディスクが一杯
DEF RES_DIR_FULL = -24; // ディレクトリが一杯
DEF RES_CANNOT_SEEK = -25; // シーク出来ない
DEF RES_SUPERVISOR = -26; // スーパバイザ状態でスーパバイザ指定
DEF RES_THREAD_EXISTS = -27; // スレッド名が存在する
DEF RES_PROCESS_BUF = -28; // プロセス間通信バッファが書き込み禁止
DEF RES_BACKGROUND = -29; // バックグラウンドプロセスを起動出来ない
DEF RES_NO_LOCKBUF = -32; // ロック領域が足りない
DEF RES_LOCKED = -33; // ロックされている
DEF RES_DRIVE_OPENED = -34; // ドライブのハンドルはオープンされている
DEF RES_TOO_MANY_LINKS = -35; // シンボリックリンクのネストが多い
DEF RES_FILE_EXISTS = -80; // ファイルが存在する
#undef DEF
struct FILES {
uint8 sattr; // $00: 検索する属性
uint8 drive; // $01: ドライブ番号
uint32 sector; // $02: ディレクトリのセクタ番号
// (DOS _FILES の先頭アドレスで代用)
//uint16 sector2; // $06: 詳細不明、未使用
uint16 offset; // $08: ディレクトリの位置、詳細不明
//char[8] name1; // $0a: 作業用ファイル名、未使用
//char[3] ext; // $12: 作業用拡張子、未使用
uint8 attr; // $15: ファイル属性
uint16 time; // $16: 最終更新時刻
uint16 date; // $18: 最終更新日付
uint32 size; // $1a: ファイルサイズ
//char[23] name; // $1e: ファイル名(フル)
// ファイル名。
// マルチバイト文字をサポートした場合はここはホスト文字コードの
// ファイル名なので、ゲスト文字コード(Shift_JIS)で23バイトに
// 収まってもホスト文字コードで23バイトで収まるとは限らない。
std::string name;
// (parent を通じて) 必要なフィールドだけを VM メモリに書き出す。
void WriteToMem(WindrvDevice *parent, uint32 addr) const;
};
class FCB
{
// +$00, 6.B: (内部使用)
// +$06, 1.L: ファイルポインタ
// +$0a, 1.L: (内部使用)
// +$0e, 1.B: オープンモード
// +$0f, 21.B: (内部使用)
// +$24, 8.B: ファイル名1
// +$2c, 3.B: 拡張子
// +$2f, 1.B: ファイル属性
// +$30, 10.B: ファイル名2
// +$3a, 1.W: 最終更新時刻
// +$3c, 1.W: 最終更新日付
// +$3e, 1.W: (内部使用)
// +$40, 1.L: ファイルサイズ
// +$44, 7.L: (内部使用)
public:
FCB(WindrvDevice *parent_, uint32 addr_);
// ここにコピーは持たず、都度 VM メモリを参照/更新する。
// FCB はフィールドが多いわりに Open、Read などの各コマンドの
// フィールドアクセスはまばらななので。
uint32 GetPos() const; // 現在位置取得
void SetPos(uint32); // 現在位置設定
void AddPos(uint32); // 現在位置を加算
uint8 GetMode() const; // オープンモード取得
void SetDateTime(uint32); // 日付時刻を設定
uint32 GetSize() const; // サイズ取得
void SetSize(uint32); // サイズ設定
void SetName(const std::string& name1, const std::string& name2,
const std::string& ext); // ファイル名を設定
uint32 GetAddr() const { return addr; }
protected:
WindrvDevice *parent {};
private:
uint32 addr {}; // FCB アドレス(検索キー)
};
struct capacity {
uint16 avail_clusters; // 空きクラスタ数
uint16 total_clusters; // 総クラスタ数
uint16 sectors_per_cluster;
uint16 bytes_per_sector;
// (parent を通じて) VM メモリに書き出す。
void WriteToMem(WindrvDevice *parent, uint32 addr) const;
};
struct DPB {
uint16 sector_size; // 1セクタあたりのバイト数
uint8 cluster_size; // 1クラスタあたりのセクタ数 - 1
uint8 shift; // クラスタからセクタへのシフト数
// (bit7=%1 なら 16bit FAT ?)
uint16 fat_sector; // FAT の先頭セクタ番号
uint8 fat_max; // FAT 領域の個数
uint8 fat_size; // 1つの FAT 領域のセクタ数
uint16 file_max; // ルートディレクトリの最大ファイル数
uint16 data_sector; // データ領域の先頭セクタ
uint16 cluster_max; // 総クラスタ数
uint16 root_sector; // ルートディレクトリの先頭セクタ番号
uint8 media; // メディア ID
// (parent を通じて) VM メモリに書き出す。
void WriteToMem(WindrvDevice *parent, uint32 addr) const;
};
// 属性をデバッグ表示用文字列にする。
static std::string AttrStr(uint8 attr);
// オープンモードを表示用文字列にする。
static std::string OpenModeStr(uint8 mode);
// ステータスコード/エラーコードに対応するメッセージを返す。
static const char *GetResultMsg(uint32 result);
// UNIX 時刻から MS-DOS 形式の日付時刻を取得。
// uint32 の上位が日付、下位が時刻で返す。
static uint32 Unixtime2DateTime(time_t);
// MS-DOS 形式の日付時刻から UNIX 時刻を取得。
static time_t DateTime2Unixtime(uint32);
private:
static const char * const result_msg[];
static const char * const err_msg[];
};
// 仮想ディレクトリエントリ
class VDirent
{
public:
VDirent(const std::string& name_, bool isdir_);
// ここの属性は Valid かどうかと Dir/Archive(Regular) のみを使う。
// Files/NFiles で拾う時は (タイムスタンプやサイズとともに)
// 属性も改めてファイルシステムから拾う。
bool IsValid() const { return isvalid; }
bool IsDir() const { return isdir; }
bool IsArchive() const { return !isdir; }
// このエントリを Human68k に見せないようにする。
void Invalidate() { isvalid = false; }
std::string name {}; // ホストファイル名。
bool isdir {}; // ディレクトリなら true、ファイルなら false。
bool isvalid {}; // エントリが有効なら true。
};
// 仮想ディレクトリ構造
class VDir
{
public:
VDir();
VDir(HostWindrv *host_, const std::string& path_);
// ホスト相対パスを返す。
const std::string& GetPath() const { return path; }
// このディレクトリから name にマッチするエントリを返す。
// 見付からなければ NULL を返す。
const VDirent *MatchName(const std::string& name, bool want_exist) const;
void Acquire();
void Release();
// 参照カウントを返す。
int Refcount() const { return refcount; }
// この VDir 構成時の mtime [nsec] を返す。
uint64 GetMTime() const { return mtime; }
// mtime を更新する。
void SetMTime(uint64 mtime_) { mtime = mtime_; }
// この VDir の参照時刻 [usec] を返す。
uint64 GetATime() const { return atime; }
// 参照時刻を更新する。
void UpdateATime();
// このディレクトリ内のエントリ。
std::vector<VDirent> list {};
private:
// ホストでの相対パス。
std::string path {};
// ホストの対応ディレクトリの更新日時 [nsec]。
// (st_mtimespec がある場合のみ使用する)
uint64 mtime {};
// この VDir を参照した実時刻 [usec]。
// こっちは、キャッシュから追い出す人を決定するために使うので uint64。
uint64 atime {};
// 参照中のディレクトリを消さないようにするため。
int refcount {};
HostWindrv *host {};
};
// VDir を自動変数みたいに勝手にクローズしてほしい。
class AutoVDir
{
public:
AutoVDir(HostWindrv *host_, VDir *vdir_) {
host = host_;
vdir = vdir_;
}
~AutoVDir();
// vdir-> で VDir* をアクセスすることで透過っぽく見せる。
VDir *operator->() const noexcept {
return vdir;
}
// unique_ptr と同様に (bool) で有効かどうかを返す。
explicit operator bool() const noexcept {
return (vdir != NULL);
}
private:
HostWindrv *host {};
VDir *vdir {};
};
// FILES のこちら側のバッファ。
class FilesBuf
{
public:
FilesBuf(uint32 key_, HostWindrv *host_);
void Init(uint32 key_, HostWindrv *host_);
uint64 GetATime() const { return atime; }
void UpdateATime();
public:
uint32 key {}; // FILES バッファアドレス
HostWindrv *host {}; // ホスト (ドライブ)
std::string path {}; // ホスト相対パス
// マッチしたファイル名一覧。
// Files/NFiles で取得するたびに先頭から削除していく方式。
std::deque<std::string> list {};
// 直近にマッチしたファイル名(ログ出力用)
std::string fname {};
private:
uint64 atime {};
};
class Windrv
{
public:
// Human68k の FCB 構造体とホスト側で必要なメンバを集約したクラス。
// ハンドルのように使う。
class FCB : public Human68k::FCB
{
using inherited = Human68k::FCB;
public:
FCB(WindrvDevice *, uint32 addr_, HostWindrv *host_);
void Close();
// 対応するホストドライバ
HostWindrv *host {};
// ファイルディスクリプタ
int fd {};
};
// いずれオプションになるかも知れないが。
static const bool option_case_sensitive = false;
};
class WindrvDevice : public IODevice, Windrv
{
using inherited = IODevice;
friend class Human68k;
friend class Windrv;
// Human68k の NAMESTS 構造体を読み込んだもの。
// windrv.sys では、Windrv デバイスから windrv.sys にこの構造体を返す
// ケースはなく、すべて windrv.sys から Windrv デバイスへの方向なので、
// 受け取った時点でホスト側に都合のいいように加工して保持する。
class NAMESTS
{
// +$00, 1.B: ワイルドカード文字数
// +$01, 1.B: ドライブ番号 (A:=0、B:=1、…)
// +$02,65.B: パス
// +$43, 8.B: ファイル名1 (余りは ' ' で埋める)
// +$4b, 3.B: 拡張子 (余りは ' ' で埋める)
// +$4e,10.B: ファイル名2 (余りは '\0' で埋める)
//
// NAMESTS 内のパスは '\' 区切り、最後にも '\' が付いて '\0' で終端、
// らしいが、観測してると '\t'(\x09) 区切りで最後にも '\t' が付いて
// '\0' で終端する文字列が来てる。なんだべ。
public:
static const int Size = 0x58; // 構造体の大きさ
private:
std::string path; // '/' 区切り
std::string name1; // パディングの ' ' を除いたもの
std::string ext; // パディングの ' ' を除いたもの
std::string name2; // パディングの '\0' を除いたもの
public:
int wildcard;
int drive;
public:
// コンストラクタ
NAMESTS(WindrvDevice *, uint32 addr);
// drive, path を '/' 区切りで返す。
std::string GetDrivePath() const;
// path のみ('/' 区切り)を返す。
const std::string& GetPath() const { return path; }
// ファイル名(8+10.3)を返す。
std::string GetName() const;
const std::string& GetName1() const { return name1; }
const std::string& GetName2() const { return name2; }
const std::string& GetExt() const { return ext; }
// ドライブ、パス、ファイル名を返す。
std::string GetFull() const;
// ログ表示用のドライブ、パス文字列を返す。
std::string PrintDrivePath() const;
};
// 最大ドライブ数
static const int MaxDrives = 1;
// FilesBuf の最大数
static const int MaxFilesBuf = 32; // (適当)
public:
WindrvDevice();
~WindrvDevice() override;
bool Create() override;
void SetLogLevel(int) override;
bool Init() override;
void ResetHard(bool poweron) override;
// Windrv を組み込むなら true を返す。
static bool IsWindrv();
protected:
// BusIO インタフェース
static const uint32 NPORT = 0x2000;
busdata ReadPort(uint32 offset);
busdata WritePort(uint32 offset, uint32 data);
busdata PeekPort(uint32 offset);
bool PokePort(uint32 offset, uint32 data);
private:
uint32 GetIdent() const;
void Execute();
uint32 DispatchCommand();
uint32 CmdInitialize();
uint32 CmdCheckDir(HostWindrv *);
uint32 CmdMakeDir(HostWindrv *);
uint32 CmdRemoveDir(HostWindrv *);
uint32 CmdRename(HostWindrv *);
uint32 CmdDelete(HostWindrv *);
uint32 CmdAttribute(HostWindrv *);
uint32 CmdFiles(HostWindrv *);
uint32 CmdNFiles(HostWindrv *);
uint32 CmdCreate(HostWindrv *);
uint32 CmdOpen(HostWindrv *);
uint32 CmdClose(HostWindrv *);
uint32 CmdRead(HostWindrv *);
uint32 CmdWrite(HostWindrv *);
uint32 CmdSeek(HostWindrv *);
uint32 CmdFileTime(HostWindrv *);
uint32 CmdGetCapacity(HostWindrv *);
uint32 CmdCtrlDrive(HostWindrv *);
uint32 CmdGetDPB(HostWindrv *);
uint32 CmdDiskRead(HostWindrv *);
uint32 CmdDiskWrite(HostWindrv *);
uint32 CmdIoControl(HostWindrv *);
uint32 CmdFlush(HostWindrv *);
uint32 CmdCheckMedia(HostWindrv *);
uint32 CmdLock(HostWindrv *);
void SetResult(uint32);
// result を表示用に整形して返す
static std::string ResultStr(uint32 result);
// メモリアクセス
uint32 ReadMem8(uint32 addr) const;
void WriteMem8(uint32 addr, uint32 data);
uint32 ReadMem16(uint32 addr) const;
void WriteMem16(uint32 addr, uint32 data);
uint32 ReadMem32(uint32 addr) const;
void WriteMem32(uint32 addr, uint32 data);
uint32 ReadMemAddr(uint32 addr) const;
void WriteMemAddr(uint32 addr, uint32 data);
void ReadMem(void *dst, uint32 srcaddr, int len) const;
void WriteMem(uint32 dstaddr, const void *src, int len);
uint32 FilesCommon(FilesBuf *, uint32 addr);
bool MatchFilename(const std::string& fname, const NAMESTS& ns) const;
static bool MatchWildcard(const std::string& fn, const std::string& pn);
// addr に対応する FILES バッファを探す。
FilesBuf *SearchFilesBuf(uint32 addr) const;
// addr に対応する FILES バッファを作成して返す。
FilesBuf *AllocFilesBuf(uint32 addr, HostWindrv *);
// addr に対応する FCB を探す。
Windrv::FCB *SearchFCB(uint32 addr) const;
// FCB を登録する。
uint32 AddFCB(const Windrv::FCB&);
// FCB を解放して削除する。
void FreeFCB(Windrv::FCB *);
uint32 a5 {};
uint32 command {};
// ログ表示用
std::string cmdname {};
std::array<std::unique_ptr<HostWindrv>, MaxDrives> hosts /*{}*/;
std::map<uint32, FilesBuf*> filesmap {};
std::map<uint32, FCB*> fcbmap {};
MainbusDevice *mainbus {};
MPU680x0Device *mpu680x0 {};
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.