File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / windrv.h
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
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 {};
};

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.