Annotation of nono/vm/windrv.cpp, revision 1.1

1.1     ! root        1: //
        !             2: // nono
        !             3: // Copyright (C) 2023 nono project
        !             4: // Licensed under nono-license.txt
        !             5: //
        !             6: 
        !             7: //
        !             8: // Windrv (相当)
        !             9: //
        !            10: 
        !            11: // Windrv インスタンスは、設定 (Windrv の有無) によらず常に生成するが、
        !            12: // メモリにマップするかどうかが変わる。
        !            13: // Windrv 設定がない時にインスタンスごと作らない方式だと、設定を無効にすると
        !            14: // 同時にログ識別子も無効になってコマンドラインの -L windrv オプションも
        !            15: // エラーになってしまう。これは操作性がよくないので WindrvDevice の
        !            16: // インスタンスは常に存在してほしい。一方、常にメモリマップ上に載せておくと
        !            17: // Windrv を無効にしているにも関わらずメモリマップモニタで表示されるのが
        !            18: // 気になることになるため。
        !            19: 
        !            20: #if !defined(TEST_WINDRV)
        !            21: 
        !            22: #include "windrv.h"
        !            23: #include "config.h"
        !            24: #include "hostwindrv.h"
        !            25: #include "mpu680x0.h"
        !            26: #include <fcntl.h>
        !            27: #include <sys/time.h>
        !            28: #include <algorithm>
        !            29: 
        !            30: // コンストラクタ
        !            31: WindrvDevice::WindrvDevice()
        !            32:        : inherited(OBJ_WINDRV)
        !            33: {
        !            34:        for (int i = 0; i < hosts.size(); i++) {
        !            35:                hosts[i].reset();
        !            36:        }
        !            37: }
        !            38: 
        !            39: // デストラクタ
        !            40: WindrvDevice::~WindrvDevice()
        !            41: {
        !            42:        for (auto& host : hosts) {
        !            43:                if ((bool)host) {
        !            44:                        host.reset();
        !            45:                }
        !            46:        }
        !            47: }
        !            48: 
        !            49: // 設定ファイルを読んで Windrv を組み込む場合は true を返す。
        !            50: /*static*/ bool
        !            51: WindrvDevice::IsWindrv()
        !            52: {
        !            53:        const ConfigItem& item = gConfig->Find("windrv-path");
        !            54:        return (item.AsString().empty() == false);
        !            55: }
        !            56: 
        !            57: bool
        !            58: WindrvDevice::Create()
        !            59: {
        !            60:        if (IsWindrv()) {
        !            61:                // 対応するホストドライバを作成。
        !            62:                // 今はまだ1つのみ。
        !            63:                hosts[0].reset(new HostWindrv(this));
        !            64:                if ((bool)hosts[0] == false) {
        !            65:                        return false;
        !            66:                }
        !            67:        }
        !            68: 
        !            69:        return true;
        !            70: }
        !            71: 
        !            72: void
        !            73: WindrvDevice::SetLogLevel(int loglevel_)
        !            74: {
        !            75:        inherited::SetLogLevel(loglevel_);
        !            76: 
        !            77:        for (auto& host : hosts) {
        !            78:                if ((bool)host) {
        !            79:                        host->SetLogLevel(loglevel_);
        !            80:                }
        !            81:        }
        !            82: }
        !            83: 
        !            84: // 初期化
        !            85: bool
        !            86: WindrvDevice::Init()
        !            87: {
        !            88:        if (inherited::Init() == false) {
        !            89:                return false;
        !            90:        }
        !            91: 
        !            92:        mainbus = GetMainbusDevice();
        !            93:        mpu680x0 = GetMPU680x0Device(mpu);
        !            94: 
        !            95:        // 今は1つしかない。
        !            96:        HostWindrv *host = hosts[0].get();
        !            97:        if (host != NULL) {
        !            98:                const ConfigItem& item = gConfig->Find("windrv-path");
        !            99:                // パスが空ならここには来ないはず。
        !           100:                assert(item.AsString().empty() == false);
        !           101:                std::string path = gMainApp.NormalizePath(item.AsString());
        !           102:                host->InitRootPath(path);
        !           103:        }
        !           104: 
        !           105:        return true;
        !           106: }
        !           107: 
        !           108: void
        !           109: WindrvDevice::ResetHard(bool poweron)
        !           110: {
        !           111:        // FILES 管理バッファをクリア。
        !           112:        for (auto& it : filesmap) {
        !           113:                delete it.second;
        !           114:        }
        !           115:        filesmap.clear();
        !           116: 
        !           117:        // FCB バッファをクリア。
        !           118:        for (auto& it : fcbmap) {
        !           119:                delete it.second;
        !           120:        }
        !           121:        fcbmap.clear();
        !           122: }
        !           123: 
        !           124: busdata
        !           125: WindrvDevice::Read8(uint32 addr)
        !           126: {
        !           127:        busdata data;
        !           128: 
        !           129:        switch (addr) {
        !           130:         case 0xe9f000: // 識別ポート
        !           131:                data = GetIdent();
        !           132:                putlog(2, "Ident -> $%02x", data.Data());
        !           133:                break;
        !           134:         default:               // 他はバスエラー
        !           135:                data.SetBusErr();
        !           136:                break;
        !           137:        }
        !           138:        return data;
        !           139: }
        !           140: 
        !           141: busdata
        !           142: WindrvDevice::Write8(uint32 addr, uint32 data)
        !           143: {
        !           144:        switch (addr) {
        !           145:         case 0xe9f000: // 動作ポート
        !           146:                // 実行
        !           147:                Execute();
        !           148:                return 0;
        !           149:         default:               // 他はバスエラー
        !           150:                return busdata::BusErr;
        !           151:        }
        !           152: }
        !           153: 
        !           154: busdata
        !           155: WindrvDevice::Peek8(uint32 addr)
        !           156: {
        !           157:        switch (addr) {
        !           158:         case 0xe9f000: // 識別ポート
        !           159:                return GetIdent();
        !           160:         default:               // 他はバスエラー
        !           161:                return busdata::BusErr;
        !           162:        }
        !           163: }
        !           164: 
        !           165: // モード識別ポートの読み出し
        !           166: uint32
        !           167: WindrvDevice::GetIdent() const
        !           168: {
        !           169:        return 'W';     // WINDRV 互換
        !           170: }
        !           171: 
        !           172: // コマンド実行
        !           173: void
        !           174: WindrvDevice::Execute()
        !           175: {
        !           176:        uint32 error;
        !           177: 
        !           178:        // A5 レジスタ経由で受け取るらしい。
        !           179:        a5 = mpu680x0->reg.A[5];
        !           180: 
        !           181:        // エラー情報をクリア。
        !           182:        WriteMem16(a5 + 3, 0);
        !           183: 
        !           184:        // コマンド番号取得。
        !           185:        command = ReadMem8(a5 + 2);
        !           186: 
        !           187:        cmdname = "Cmd";
        !           188:        if (__predict_false(loglevel >= 2)) {
        !           189:                cmdname += string_format(" $%02x", command);
        !           190:        }
        !           191: 
        !           192:        if (command == 0x40) {
        !           193:                // 初期化コマンドだけいろいろ挙動が違う。
        !           194:                CmdInitialize();
        !           195:                error = 0;
        !           196:        } else {
        !           197:                // コマンド実行。
        !           198:                uint32 result = DispatchCommand();
        !           199: 
        !           200:                // 結果を整理。
        !           201:                SetResult(result);
        !           202: 
        !           203:                // 戻り値は result ではなくエラー情報のようだ。
        !           204:                error = ReadMem8(a5 + 3) | (ReadMem8(a5 + 4) << 8);
        !           205:        }
        !           206: 
        !           207:        mpu680x0->reg.D[0] = error;
        !           208: }
        !           209: 
        !           210: // コマンドに分岐。
        !           211: uint32
        !           212: WindrvDevice::DispatchCommand()
        !           213: {
        !           214:        uint32 result;
        !           215: 
        !           216:        // ユニット番号取得。
        !           217:        int unit = ReadMem8(a5 + 1);
        !           218: 
        !           219:        // 初期化コマンド以外ならユニット番号をチェック。
        !           220:        if (unit >= hosts.size()) {
        !           221:                return -Human68k::ERR_INVALID_UNIT_IA;
        !           222:        }
        !           223:        HostWindrv *host = hosts[unit].get();
        !           224:        if (host == NULL) {
        !           225:                return -Human68k::ERR_INVALID_UNIT_IA;
        !           226:        }
        !           227: 
        !           228:        // 最上位ビットはベリファイフラグのようだ。(無視する)
        !           229:        switch (command & 0x7f) {
        !           230:         case 0x41:     result = CmdCheckDir(host);             break;  // ディレクトリチェック
        !           231:         case 0x42:     result = CmdMakeDir(host);              break;  // ディレクトリ作成
        !           232:         case 0x43:     result = CmdRemoveDir(host);    break;  // ディレクトリ削除
        !           233:         case 0x44:     result = CmdRename(host);               break;  // ファイル名変更
        !           234:         case 0x45:     result = CmdDelete(host);               break;  // ファイル削除
        !           235:         case 0x46:     result = CmdAttribute(host);    break;  // ファイル属性取得設定
        !           236:         case 0x47:     result = CmdFiles(host);                break;  // ファイル検索(First)
        !           237:         case 0x48:     result = CmdNFiles(host);               break;  // ファイル検索(Next)
        !           238:         case 0x49:     result = CmdCreate(host);               break;  // ファイル作成
        !           239:         case 0x4a:     result = CmdOpen(host);                 break;  // ファイルオープン
        !           240:         case 0x4b:     result = CmdClose(host);                break;  // ファイルクローズ
        !           241:         case 0x4c:     result = CmdRead(host);                 break;  // ファイル読み込み
        !           242:         case 0x4d:     result = CmdWrite(host);                break;  // ファイル書き込み
        !           243:         case 0x4e:     result = CmdSeek(host);                 break;  // ファイルシーク
        !           244:         case 0x4f:     result = CmdFileTime(host);             break;  // ファイル更新時刻
        !           245:         case 0x50:     result = CmdGetCapacity(host);  break;  // 容量取得
        !           246:         case 0x51:     result = CmdCtrlDrive(host);    break;  // ドライブ制御/状態検査
        !           247:         case 0x52:     result = CmdGetDPB(host);               break;  // DPB取得
        !           248:         case 0x53:     result = CmdDiskRead(host);             break;  // セクタ読み込み
        !           249:         case 0x54:     result = CmdDiskWrite(host);    break;  // セクタ書き込み
        !           250:         case 0x55:     result = CmdIoControl(host);    break;  // IOCTRL
        !           251:         case 0x56:     result = CmdFlush(host);                break;  // フラッシュ
        !           252:         case 0x57:     result = CmdCheckMedia(host);   break;  // メディア交換チェック
        !           253:         case 0x58:     result = CmdLock(host);                 break;  // 排他制御
        !           254:         default:
        !           255:                // 無効なコマンド
        !           256:                putlog(1, "Cmd $%02x (INVALID)", command);
        !           257:                result = -Human68k::ERR_INVALID_COMMAND_IA;
        !           258:                break;
        !           259:        }
        !           260:        return result;
        !           261: }
        !           262: 
        !           263: // $40 初期化
        !           264: //
        !           265: // IN
        !           266: //   +0, 1.B: 定数 ($16)
        !           267: //      +2, 1.B: コマンド ($40)
        !           268: //     +18, 1.L: 引数が書いてあるアドレス
        !           269: //     +22, 1.B: (先頭の) ドライブ番号 (A:=0、…)
        !           270: // OUT
        !           271: //      +3, 1.W: エラーコード(下位、上位の順)
        !           272: //     +13, 1.B: ユニット数
        !           273: //     +14, 1.L: デバイスドライバの終了アドレス + 1
        !           274: uint32
        !           275: WindrvDevice::CmdInitialize()
        !           276: {
        !           277:        if (__predict_false(loglevel >= 1)) {
        !           278:                cmdname += " Initialize";
        !           279:                putlog(2, "%s", cmdname.c_str());
        !           280:        }
        !           281: 
        !           282:        // ベースドライブ名を取得。
        !           283:        uint32 base = ReadMem8(a5 + 22);
        !           284:        if (base >= 26) {
        !           285:                uint32 result = Human68k::RES_INVALID_DRIVE;
        !           286:                putlog(2, "%s => %s", cmdname.c_str(), ResultStr(result).c_str());
        !           287:                return result;
        !           288:        }
        !           289: 
        !           290:        int drives = 0;
        !           291:        for (auto& host : hosts) {
        !           292:                if ((bool)host) {
        !           293:                        uint32 result = host->Initialize();
        !           294:                        if ((int32)result < 0) {
        !           295:                                return result;
        !           296:                        }
        !           297:                        drives++;
        !           298:                }
        !           299:        }
        !           300: 
        !           301:        if (loglevel == 1) {
        !           302:                putlogn("%s => %c:", cmdname.c_str(), 'A' + base);
        !           303:        } else {
        !           304:                putlog(2, "%s Base=%c: Drives=%d", cmdname.c_str(), 'A' + base, drives);
        !           305:        }
        !           306: 
        !           307:        WriteMem8(a5 + 13, drives);
        !           308:        return 0;
        !           309: }
        !           310: 
        !           311: // $41 ディレクトリチェック
        !           312: //
        !           313: // IN
        !           314: //   +0, 1.B: 定数 ($1a)
        !           315: //   +1, 1.B: ユニット番号
        !           316: //   +2, 1.B: コマンド ($41/$c1)
        !           317: //     +14, 1.L: NAMESTS 構造体のアドレス
        !           318: // OUT
        !           319: //   +3, 1.W: エラーコード (下位、上位の順)
        !           320: //  +18, 1.L: ステータスコード
        !           321: //
        !           322: // NAMESTS の drive:path に存在を確認したいディレクトリのパス。
        !           323: uint32
        !           324: WindrvDevice::CmdCheckDir(HostWindrv *host)
        !           325: {
        !           326:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           327: 
        !           328:        if (__predict_false(loglevel >= 1)) {
        !           329:                cmdname += string_format(" CheckDir %s", ns.PrintDrivePath().c_str());
        !           330:                putlog(2, "%s", cmdname.c_str());
        !           331:        }
        !           332: 
        !           333:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           334:        if ((bool)vdir == false) {
        !           335:                return Human68k::RES_DIR_NOT_FOUND;
        !           336:        }
        !           337:        return 0;
        !           338: }
        !           339: 
        !           340: // $42 ディレクトリ作成
        !           341: //
        !           342: // IN
        !           343: //   +0, 1.B: 定数 ($1a)
        !           344: //   +1, 1.B: ユニット番号
        !           345: //   +2, 1.B: コマンド ($42/$c2)
        !           346: //     +14, 1.L: NAMESTS 構造体のアドレス
        !           347: // OUT
        !           348: //   +3, 1.W: エラーコード (下位、上位の順)
        !           349: //  +18, 1.L: ステータスコード
        !           350: //
        !           351: // NAMESTS の drive:path 配下の name に作成したいディレクトリ名。
        !           352: // 同名のディレクトリがある場合はエラー。
        !           353: uint32
        !           354: WindrvDevice::CmdMakeDir(HostWindrv *host)
        !           355: {
        !           356:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           357: 
        !           358:        if (__predict_false(loglevel >= 1)) {
        !           359:                cmdname += string_format(" MakeDir %s '%s'",
        !           360:                        ns.PrintDrivePath().c_str(), ns.GetName().c_str());
        !           361:                putlog(2, "%s", cmdname.c_str());
        !           362:        }
        !           363: 
        !           364:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           365:        if ((bool)vdir == false) {
        !           366:                return Human68k::RES_DIR_NOT_FOUND;
        !           367:        }
        !           368: 
        !           369:        const VDirent *ent = vdir->MatchName(ns.GetName(), false);
        !           370:        if (ent) {
        !           371:                return Human68k::RES_FILE_EXISTS;
        !           372:        }
        !           373:        uint32 result = host->MakeDir(vdir->GetPath() + ns.GetName());
        !           374:        return result;
        !           375: }
        !           376: 
        !           377: // $43 ディレクトリ削除
        !           378: //
        !           379: // IN
        !           380: //   +0, 1.B: 定数 ($1a)
        !           381: //   +1, 1.B: ユニット番号
        !           382: //   +2, 1.B: コマンド ($43/$c3)
        !           383: //     +14, 1.L: NAMESTS 構造体アドレス
        !           384: // OUT
        !           385: //   +3, 1.W: エラーコード (下位、上位の順)
        !           386: //  +18, 1.L: ステータスコード
        !           387: //
        !           388: // NAMESTS の drive:path 配下の name に削除したいディレクトリ名。
        !           389: // ディレクトリが空でない場合はエラー。
        !           390: uint32
        !           391: WindrvDevice::CmdRemoveDir(HostWindrv *host)
        !           392: {
        !           393:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           394: 
        !           395:        if (__predict_false(loglevel >= 1)) {
        !           396:                cmdname += string_format(" RemoveDir %s '%s'",
        !           397:                        ns.PrintDrivePath().c_str(), ns.GetName().c_str());
        !           398:                putlog(2, "%s", cmdname.c_str());
        !           399:        }
        !           400: 
        !           401:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           402:        if ((bool)vdir == false) {
        !           403:                return Human68k::RES_DIR_NOT_FOUND;
        !           404:        }
        !           405: 
        !           406:        const VDirent *ent = vdir->MatchName(ns.GetName(), true);
        !           407:        if (ent == NULL) {
        !           408:                return Human68k::RES_DIR_NOT_FOUND;
        !           409:        }
        !           410:        // ディレクトリが空かどうかは RemoveDir() にお任せしてある。
        !           411:        uint32 result = host->RemoveDir(vdir->GetPath() + ent->name);
        !           412:        return result;
        !           413: }
        !           414: 
        !           415: // $44 ファイル名変更
        !           416: //
        !           417: // IN
        !           418: //   +0, 1.B: 定数 ($1a)
        !           419: //   +1, 1.B: ユニット番号
        !           420: //   +2, 1.B: コマンド ($44/$c4)
        !           421: //     +14, 1.L: NAMESTS 構造体アドレス (旧ファイル名)
        !           422: //     +18, 1.L: NAMESTS 構造体アドレス (新ファイル名)
        !           423: // OUT
        !           424: //   +3, 1.W: エラーコード (下位、上位の順)
        !           425: //  +18, 1.L: ステータスコード
        !           426: //
        !           427: // o パスが違う場合はディレクトリを移動する (ドライブはどちらも同じはず)。
        !           428: // o 新ファイル名がすでに存在する場合はエラー。
        !           429: // o システムファイル、書き込み禁止ファイルはファイル名の変更はできない。
        !           430: //   (システムファイルのディレクトリの移動を伴うファイル名の変更は可らしい)
        !           431: // o ディレクトリ、ボリュームラベル、隠しファイル、書き込み禁止ファイルは
        !           432: //   ディレクトリ間の移動はできない。
        !           433: uint32
        !           434: WindrvDevice::CmdRename(HostWindrv *host)
        !           435: {
        !           436:        NAMESTS oldns(this, ReadMemAddr(a5 + 14));
        !           437:        NAMESTS newns(this, ReadMemAddr(a5 + 18));
        !           438: 
        !           439:        if (__predict_false(loglevel >= 1)) {
        !           440:                cmdname += string_format(" Rename %s '%s' to %s '%s'",
        !           441:                        oldns.PrintDrivePath().c_str(), oldns.GetName().c_str(),
        !           442:                        newns.PrintDrivePath().c_str(), newns.GetName().c_str());
        !           443:                putlog(2, "%s", cmdname.c_str());
        !           444:        }
        !           445: 
        !           446:        // 移動元
        !           447:        AutoVDir oldvdir(host, host->OpenVDir(oldns.GetPath()));
        !           448:        if ((bool)oldvdir == false) {
        !           449:                return Human68k::RES_DIR_NOT_FOUND;
        !           450:        }
        !           451:        const VDirent *oldent = oldvdir->MatchName(oldns.GetName(), true);
        !           452:        if (oldent == NULL) {
        !           453:                return Human68k::RES_FILE_NOT_FOUND;
        !           454:        }
        !           455: 
        !           456:        // 移動先
        !           457:        AutoVDir newvdir(host, host->OpenVDir(newns.GetPath()));
        !           458:        if ((bool)newvdir == false) {
        !           459:                return Human68k::RES_DIR_NOT_FOUND;
        !           460:        }
        !           461:        const VDirent *newent = newvdir->MatchName(newns.GetName(), false);
        !           462:        if (newent) {
        !           463:                return Human68k::RES_FILE_EXISTS;
        !           464:        }
        !           465: 
        !           466:        // 移動元ファイルの属性。
        !           467:        uint32 attr = host->GetAttribute(oldvdir->GetPath() + oldent->name);
        !           468:        if ((int32)attr < 0) {
        !           469:                return attr;
        !           470:        }
        !           471:        // 書き込み禁止ファイルなら何も出来ない。
        !           472:        if ((attr & Human68k::ATTR_RDONLY)) {
        !           473:                return Human68k::RES_NOT_A_FILE;
        !           474:        }
        !           475: 
        !           476:        // ディレクトリなら、ディレクトリ間の移動は出来ない。
        !           477:        if ((attr & Human68k::ATTR_DIR)) {
        !           478:                if (oldvdir->GetPath() != newvdir->GetPath()) {
        !           479:                        return Human68k::RES_NOT_A_FILE;
        !           480:                }
        !           481:        }
        !           482: 
        !           483:        // ボリューム、システムファイル、隠しファイルはまだない。
        !           484: 
        !           485:        uint32 result = host->Rename(
        !           486:                oldvdir->GetPath() + oldent->name,
        !           487:                newvdir->GetPath() + newns.GetName());
        !           488:        return result;
        !           489: }
        !           490: 
        !           491: // $45 ファイル削除
        !           492: //
        !           493: // IN
        !           494: //   +0, 1.B: 定数 ($1a)
        !           495: //   +1, 1.B: ユニット番号
        !           496: //   +2, 1.B: コマンド ($45/$c5)
        !           497: //     +14, 1.L: NAMESTS 構造体アドレス
        !           498: // OUT
        !           499: //   +3, 1.W: エラーコード (下位、上位の順)
        !           500: //  +18, 1.L: ステータスコード
        !           501: //
        !           502: // ディレクトリ、ボリュームラベル、システムファイル、書き込み禁止ファイル
        !           503: // ならエラー。
        !           504: uint32
        !           505: WindrvDevice::CmdDelete(HostWindrv *host)
        !           506: {
        !           507:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           508: 
        !           509:        if (__predict_false(loglevel >= 1)) {
        !           510:                cmdname += string_format(" Delete %s '%s'",
        !           511:                        ns.PrintDrivePath().c_str(), ns.GetName().c_str());
        !           512:                putlog(2, "%s", cmdname.c_str());
        !           513:        }
        !           514: 
        !           515:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           516:        if ((bool)vdir == false) {
        !           517:                return Human68k::RES_DIR_NOT_FOUND;
        !           518:        }
        !           519: 
        !           520:        const VDirent *ent = vdir->MatchName(ns.GetName(), true);
        !           521:        if (ent == NULL) {
        !           522:                return Human68k::RES_FILE_NOT_FOUND;
        !           523:        }
        !           524:        uint32 result = host->Delete(vdir->GetPath() + ent->name);
        !           525:        return result;
        !           526: }
        !           527: 
        !           528: // $46 ファイル属性取得/設定
        !           529: //
        !           530: // IN
        !           531: //   +0, 1.B: 定数 ($1a)
        !           532: //   +1, 1.B: ユニット番号
        !           533: //   +2, 1.B: コマンド ($46/$c6)
        !           534: //     +13, 1.B: 設定する属性、0xff なら取得
        !           535: //     +14, 1.L: NAMESTS 構造体アドレス
        !           536: // OUT
        !           537: //   +3, 1.W: エラーコード (下位、上位の順)
        !           538: //  +18, 1.L: ステータスコード
        !           539: uint32
        !           540: WindrvDevice::CmdAttribute(HostWindrv *host)
        !           541: {
        !           542:        uint32 attr = ReadMem8(a5 + 13);
        !           543:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           544:        if (__predict_false(loglevel >= 1)) {
        !           545:                if (attr == 0xff) {
        !           546:                        cmdname += string_format(" Attribute(Get) %s '%s'",
        !           547:                                ns.PrintDrivePath().c_str(), ns.GetName().c_str());
        !           548:                } else {
        !           549:                        cmdname += string_format(" Attribute(Set) %s '%s' %s",
        !           550:                                ns.PrintDrivePath().c_str(), ns.GetName().c_str(),
        !           551:                                Human68k::AttrStr(attr).c_str());
        !           552:                }
        !           553:                putlog(2, "%s", cmdname.c_str());
        !           554:        }
        !           555: 
        !           556:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           557:        if ((bool)vdir == false) {
        !           558:                return Human68k::RES_DIR_NOT_FOUND;
        !           559:        }
        !           560: 
        !           561:        const VDirent *ent = vdir->MatchName(ns.GetName(), true);
        !           562:        if (ent == NULL) {
        !           563:                return Human68k::RES_FILE_NOT_FOUND;
        !           564:        }
        !           565: 
        !           566:        std::string fullname = vdir->GetPath() + ent->name;
        !           567:        uint32 result;
        !           568:        if (attr == 0xff) {
        !           569:                // 取得
        !           570:                result = host->GetAttribute(fullname);
        !           571:                if ((int32)result >= 0) {
        !           572:                        if (__predict_false(loglevel >= 2)) {
        !           573:                                putlogn("result=$%02x(%s)", result,
        !           574:                                        Human68k::AttrStr(result).c_str());
        !           575:                        } else if (__predict_false(loglevel == 1)) {
        !           576:                                putlogn("%s => %s", cmdname.c_str(),
        !           577:                                        Human68k::AttrStr(result).c_str());
        !           578:                        }
        !           579:                        cmdname.clear();
        !           580:                }
        !           581:        } else {
        !           582:                // 設定
        !           583:                result = host->SetAttribute(fullname, attr);
        !           584:        }
        !           585:        return result;
        !           586: }
        !           587: 
        !           588: // $47 ファイル検索(First)
        !           589: //
        !           590: // IN
        !           591: //   +0, 1.B: 定数 ($1a)
        !           592: //   +1, 1.B: ユニット番号
        !           593: //   +2, 1.B: コマンド ($47/$c7)
        !           594: //     +13, 1.B: 設定する属性、0xff なら取得
        !           595: //     +14, 1.L: NAMESTS 構造体アドレス (検索対象)
        !           596: //     +18, 1.L: FILES 構造体アドレス (検索バッファ)
        !           597: // OUT
        !           598: //      +3, 1.W: エラーコード (下位、上位の順)
        !           599: //     +18, 1.L: ステータスコード
        !           600: //
        !           601: // NAMESTS の drive:path 配下から name (ワイルドカードあり) にマッチして
        !           602: // 検索属性(+13.B) で立ててあるビットがどれか立っている(でいいのかな?)
        !           603: // エントリを探して先頭の1つを FILES に返す。
        !           604: // 見付からなければ RES_FILE_NOT_FOUND を返す。
        !           605: uint32
        !           606: WindrvDevice::CmdFiles(HostWindrv *host)
        !           607: {
        !           608:        uint8 attr = ReadMem8(a5 + 13);
        !           609:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           610:        uint32 filesaddr = ReadMemAddr(a5 + 18);
        !           611:        uint32 result;
        !           612: 
        !           613:        if (__predict_false(loglevel >= 1)) {
        !           614:                cmdname += string_format(" Files %s '%s' (%s)",
        !           615:                        ns.PrintDrivePath().c_str(), ns.GetName().c_str(),
        !           616:                        Human68k::AttrStr(attr).c_str());
        !           617:                putlog(2, "%s", cmdname.c_str());
        !           618:        }
        !           619: 
        !           620:        // 同じ FILES バッファが見付かればクリア。ボリューム検索より先に行う。
        !           621:        FilesBuf *fbuf = SearchFilesBuf(filesaddr);
        !           622:        if (fbuf) {
        !           623:                putlog(2, "Reuse FilesBuf at $%08x", filesaddr);
        !           624:                fbuf->Init(filesaddr, host);
        !           625:        }
        !           626: 
        !           627:        // ボリュームの検索はたびたび来るけどサポートしてないので先に弾く。
        !           628:        // これは検索が成功した(マッチしたのが0だっただけ)扱いのログを出す。
        !           629:        if (attr == Human68k::ATTR_VOLUME) {
        !           630:                result = Human68k::RES_LAST_FILE;
        !           631:                if (__predict_false(loglevel == 1)) {
        !           632:                        putlogn("%s", cmdname.c_str());
        !           633:                        putlogn("Cmd Files  => %s", ResultStr(result).c_str());
        !           634:                        cmdname.clear();
        !           635:                }
        !           636:                return result;
        !           637:        }
        !           638: 
        !           639:        if (fbuf == NULL) {
        !           640:                fbuf = AllocFilesBuf(filesaddr, host);
        !           641:                if (fbuf == NULL) {
        !           642:                        return Human68k::RES_NO_MEMORY;
        !           643:                }
        !           644:        }
        !           645: 
        !           646:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           647:        if ((bool)vdir == false) {
        !           648:                return Human68k::RES_DIR_NOT_FOUND;
        !           649:        }
        !           650: 
        !           651:        // FilesBuf にマッチしたエントリ一覧を作成。
        !           652:        fbuf->path = vdir->GetPath();
        !           653:        for (const VDirent& ent : vdir->list) {
        !           654:                if (__predict_false(ent.IsValid() == false)) {
        !           655:                        continue;
        !           656:                }
        !           657: 
        !           658:                // 属性が一致するか。
        !           659:                // attr に立っているビットがエントリ側にも立っているか、かな。
        !           660:                // ent のほうはディレクトリかどうかしか区別がない。
        !           661:                if (ent.IsDir()) {
        !           662:                        if ((attr & Human68k::ATTR_DIR) == 0) {
        !           663:                                continue;
        !           664:                        }
        !           665:                } else {
        !           666:                        if ((attr & Human68k::ATTR_ARCHIVE) == 0) {
        !           667:                                continue;
        !           668:                        }
        !           669:                }
        !           670: 
        !           671:                // ルートディレクトリなら "." と ".." を除外。
        !           672:                if (vdir->GetPath() == "/") {
        !           673:                        if (ent.name == "." || ent.name == "..") {
        !           674:                                continue;
        !           675:                        }
        !           676:                }
        !           677: 
        !           678:                // ファイル名が一致するか (ワイルドカードを含む)。
        !           679:                if (MatchFilename(ent.name, ns) == false) {
        !           680:                        continue;
        !           681:                }
        !           682: 
        !           683:                fbuf->list.emplace_back(ent.name);
        !           684:        }
        !           685: 
        !           686:        result = FilesCommon(fbuf, filesaddr);
        !           687:        if (result == 0) {
        !           688:                if (loglevel == 1) {
        !           689:                        putlogn("%s", cmdname.c_str());
        !           690:                        putlogn("Cmd Files  '%s'", fbuf->fname.c_str());
        !           691:                        cmdname.clear();
        !           692:                }
        !           693:        }
        !           694:        return result;
        !           695: }
        !           696: 
        !           697: // $48 ファイル検索(NFiles)
        !           698: //
        !           699: // IN
        !           700: //   +0, 1.B: 定数 ($1a)
        !           701: //   +1, 1.B: ユニット番号
        !           702: //   +2, 1.B: コマンド ($48/$c8)
        !           703: //     +18, 1.L: FILES 構造体アドレス (検索バッファ)
        !           704: // OUT
        !           705: //      +3, 1.W: エラーコード (下位、上位の順)
        !           706: //     +18, 1.L: ステータスコード
        !           707: uint32
        !           708: WindrvDevice::CmdNFiles(HostWindrv *host)
        !           709: {
        !           710:        if (__predict_false(loglevel >= 1)) {
        !           711:                cmdname += " NFiles";
        !           712:                putlog(2, "%s", cmdname.c_str());
        !           713:        }
        !           714: 
        !           715:        uint32 filesaddr = ReadMemAddr(a5 + 18);
        !           716:        FilesBuf *fbuf = SearchFilesBuf(filesaddr);
        !           717:        if (fbuf == NULL) {
        !           718:                return Human68k::RES_FILE_NOT_FOUND;
        !           719:        }
        !           720: 
        !           721:        uint32 result = FilesCommon(fbuf, filesaddr);
        !           722:        if (result == 0) {
        !           723:                if (loglevel == 1) {
        !           724:                        putlogn("%s '%s'", cmdname.c_str(), fbuf->fname.c_str());
        !           725:                        cmdname.clear();
        !           726:                }
        !           727:        }
        !           728:        return result;
        !           729: }
        !           730: 
        !           731: // Files/NFiles の共通部分。
        !           732: // FilesBuf から一つ取り出して VM メモリ filesaddr に書き出す。
        !           733: uint32
        !           734: WindrvDevice::FilesCommon(FilesBuf *fbuf, uint32 filesaddr)
        !           735: {
        !           736:        HostWindrv *host = fbuf->host;
        !           737:        assert(host);
        !           738: 
        !           739:        // 取り出せても取り出せなくても参照時刻は更新。
        !           740:        fbuf->UpdateATime();
        !           741: 
        !           742:        // 見付かった先頭の一つを返す。
        !           743:        while (fbuf->list.empty() == false) {
        !           744:                fbuf->fname = fbuf->list.front();
        !           745:                fbuf->list.pop_front();
        !           746: 
        !           747:                Human68k::FILES files;
        !           748:                if (host->GetFileStat(&files, fbuf->path + fbuf->fname) == false) {
        !           749:                        continue;
        !           750:                }
        !           751: 
        !           752:                putlog(2, "name: '%s'", fbuf->fname.c_str());
        !           753:                files.name = fbuf->fname;
        !           754:                files.sector = fbuf->key;
        !           755:                files.offset = 0;
        !           756:                files.WriteToMem(this, filesaddr);
        !           757:                return 0;
        !           758:        }
        !           759: 
        !           760:        return Human68k::RES_LAST_FILE;
        !           761: }
        !           762: 
        !           763: // $49 ファイル作成
        !           764: //
        !           765: // IN
        !           766: //   +0, 1.B: 定数 ($1a)
        !           767: //   +1, 1.B: ユニット番号
        !           768: //   +2, 1.B: コマンド ($49/$c9)
        !           769: //  +13, 1.B: ファイル属性
        !           770: //     +14, 1.L: NAMESTS 構造体アドレス
        !           771: //  +18, 1.L: モード
        !           772: //  +22, 1.L: FCB 構造体アドレス
        !           773: // OUT
        !           774: //   +3, 1.W: エラーコード (下位、上位の順)
        !           775: //  +18, 1.L: ステータスコード
        !           776: //
        !           777: // 同名ファイルがすでに存在していた場合、モードが 0 ならエラーを返す。
        !           778: // モードが 1 なら既存ファイルを削除してからファイルを作成する。
        !           779: // ボリュームラベルには未対応。
        !           780: uint32
        !           781: WindrvDevice::CmdCreate(HostWindrv *host)
        !           782: {
        !           783:        uint32 result;
        !           784: 
        !           785:        uint8 attr = ReadMem8(a5 + 13);
        !           786:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           787:        uint32 mode = ReadMem32(a5 + 18);
        !           788:        uint32 fcbaddr = ReadMemAddr(a5 + 22);
        !           789:        FCB fcb(this, fcbaddr, host);
        !           790: 
        !           791:        if (__predict_false(loglevel >= 1)) {
        !           792:                cmdname += string_format(" Create $%06x %s '%s' (%s) mode=%d",
        !           793:                        fcbaddr, ns.PrintDrivePath().c_str(), ns.GetName().c_str(),
        !           794:                        Human68k::AttrStr(attr).c_str(), mode);
        !           795:                putlog(2, "%s", cmdname.c_str());
        !           796:        }
        !           797: 
        !           798:        // 同じ FCB が見付かれば先に閉じてしまうか。
        !           799:        FCB *oldfcb = SearchFCB(fcbaddr);
        !           800:        if (oldfcb) {
        !           801:                FreeFCB(oldfcb);
        !           802:        }
        !           803: 
        !           804:        // ボリュームラベルには未対応。
        !           805:        if (attr == Human68k::ATTR_VOLUME) {
        !           806:                return Human68k::RES_NOT_A_FILE;
        !           807:        }
        !           808: 
        !           809:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           810:        if ((bool)vdir == false) {
        !           811:                return Human68k::RES_DIR_NOT_FOUND;
        !           812:        }
        !           813: 
        !           814:        // 同名のファイルが(1つ以上)存在していた場合。
        !           815:        if (vdir->MatchName(ns.GetName(), false) != NULL) {
        !           816:                if (mode == 0) {
        !           817:                        // モード 0 ならエラーにする。
        !           818:                        return Human68k::RES_FILE_EXISTS;
        !           819:                } else {
        !           820:                        // モード 1 なら削除して作成なので既存ファイルが1つでないと困る。
        !           821:                        const VDirent *ent = vdir->MatchName(ns.GetName(), true);
        !           822:                        if (ent == NULL) {
        !           823:                                // 複数候補があると消そうにも消せない…。
        !           824:                                return Human68k::RES_FILE_EXISTS;
        !           825:                        } else {
        !           826:                                // 既存ファイルは1つなので削除する。
        !           827:                                result = host->Delete(vdir->GetPath() + ent->name);
        !           828:                                if ((int32)result < 0) {
        !           829:                                        return result;
        !           830:                                }
        !           831:                        }
        !           832:                }
        !           833:        }
        !           834: 
        !           835:        const std::string fullname = vdir->GetPath() + ns.GetName();
        !           836:        result = host->CreateFile(&fcb, fullname, attr);
        !           837:        if ((int32)result < 0) {
        !           838:                return result;
        !           839:        }
        !           840: 
        !           841:        // ここで内部に登録。
        !           842:        // ディスクリプタもコピーされるがただの整数なので問題ない。ほんとだろうか。
        !           843:        return AddFCB(fcb);
        !           844: }
        !           845: 
        !           846: // $4a ファイルオープン
        !           847: //
        !           848: // IN
        !           849: //   +0, 1.B: 定数 ($1a)
        !           850: //   +1, 1.B: ユニット番号
        !           851: //   +2, 1.B: コマンド ($4a/$ca)
        !           852: //     +14, 1.L: NAMESTS 構造体アドレス
        !           853: //  +22, 1.L: FCB 構造体アドレス
        !           854: // OUT
        !           855: //   +3, 1.W: エラーコード (下位、上位の順)
        !           856: //  +18, 1.L: ステータスコード
        !           857: //
        !           858: // o 成功すれば、FCB のファイル名、更新日時、ファイルサイズを更新する。
        !           859: // o ディレクトリ、ボリュームラベルはオープンできない。
        !           860: // o システムファイル、書き込み禁止ファイルは読み込みモード以外ではオープン
        !           861: //   できない。
        !           862: uint32
        !           863: WindrvDevice::CmdOpen(HostWindrv *host)
        !           864: {
        !           865:        NAMESTS ns(this, ReadMemAddr(a5 + 14));
        !           866:        uint32 fcbaddr = ReadMemAddr(a5 + 22);
        !           867:        FCB fcb(this, fcbaddr, host);
        !           868:        uint8 mode = fcb.GetMode();
        !           869: 
        !           870:        if (__predict_false(loglevel >= 1)) {
        !           871:                cmdname += string_format(" Open $%06x %s '%s' %s",
        !           872:                        fcbaddr, ns.PrintDrivePath().c_str(), ns.GetName().c_str(),
        !           873:                        Human68k::OpenModeStr(mode).c_str());
        !           874:                putlog(2, "%s", cmdname.c_str());
        !           875:        }
        !           876: 
        !           877:        // 同じ FCB が見付かれば先に閉じてしまうか。
        !           878:        FCB *oldfcb = SearchFCB(fcbaddr);
        !           879:        if (oldfcb) {
        !           880:                FreeFCB(oldfcb);
        !           881:        }
        !           882: 
        !           883:        AutoVDir vdir(host, host->OpenVDir(ns.GetPath()));
        !           884:        if ((bool)vdir == false) {
        !           885:                return Human68k::RES_DIR_NOT_FOUND;
        !           886:        }
        !           887: 
        !           888:        const VDirent *ent = vdir->MatchName(ns.GetName(), true);
        !           889:        if (ent == NULL) {
        !           890:                return Human68k::RES_FILE_NOT_FOUND;
        !           891:        }
        !           892: 
        !           893:        // エントリがディレクトリかどうかはこの時点で分かる。
        !           894:        // ボリュームラベルは現状存在しない。
        !           895:        if (ent->IsDir()) {
        !           896:                return Human68k::RES_NOT_A_FILE;
        !           897:        }
        !           898: 
        !           899:        const std::string fullname = vdir->GetPath() + ent->name;
        !           900:        if (mode != Human68k::OPEN_RDONLY) {
        !           901:                // 書き込みを伴うなら、ファイルの属性を確認。
        !           902:                uint32 attr = host->GetAttribute(fullname);
        !           903:                if ((int32)attr < 0) {
        !           904:                        return Human68k::RES_NOT_A_FILE;
        !           905:                }
        !           906: 
        !           907:                if ((attr & Human68k::ATTR_RDONLY)) {
        !           908:                        return Human68k::RES_CANNOT_WRITE;
        !           909:                }
        !           910:        }
        !           911: 
        !           912:        uint32 result = host->Open(&fcb, fullname);
        !           913:        if ((int32)result < 0) {
        !           914:                return result;
        !           915:        }
        !           916: 
        !           917:        // タイムスタンプとサイズは Open() がセットしている。
        !           918:        // 名前はこっちでセットする。
        !           919:        fcb.SetName(ns.GetName1(), ns.GetName2(), ns.GetExt());
        !           920: 
        !           921:        // ここで内部に登録。
        !           922:        // ディスクリプタもコピーされるがただの整数なので問題ない。ほんとだろうか。
        !           923:        return AddFCB(fcb);
        !           924: }
        !           925: 
        !           926: // $4b ファイルクローズ
        !           927: //
        !           928: // IN
        !           929: //   +0, 1.B: 定数 ($1a)
        !           930: //   +1, 1.B: ユニット番号
        !           931: //   +2, 1.B: コマンド ($4b/$cb)
        !           932: //  +22, 1.L: FCB 構造体アドレス
        !           933: // OUT
        !           934: //   +3, 1.W: エラーコード (下位、上位の順)
        !           935: //  +18, 1.L: ステータスコード
        !           936: uint32
        !           937: WindrvDevice::CmdClose(HostWindrv *host)
        !           938: {
        !           939:        uint32 fcbaddr = ReadMemAddr(a5 + 22);
        !           940: 
        !           941:        if (__predict_false(loglevel >= 1)) {
        !           942:                cmdname += string_format(" Close $%06x", fcbaddr);
        !           943:                putlog(2, "%s", cmdname.c_str());
        !           944:        }
        !           945: 
        !           946:        FCB *fcb = SearchFCB(fcbaddr);
        !           947:        if (fcb == NULL) {
        !           948:                return Human68k::RES_INVALID_MEM; // ?
        !           949:        }
        !           950: 
        !           951:        FreeFCB(fcb);
        !           952:        return 0;
        !           953: }
        !           954: 
        !           955: // $4c ファイル読み込み
        !           956: //
        !           957: // IN
        !           958: //   +0, 1.B: 定数 ($1a)
        !           959: //   +1, 1.B: ユニット番号
        !           960: //   +2, 1.B: コマンド ($4c/$cc)
        !           961: //     +14, 1.L: 読み込み先アドレス
        !           962: //  +18, 1.L: 読み込みバイト数
        !           963: //  +22, 1.L: FCB 構造体アドレス
        !           964: // OUT
        !           965: //   +3, 1.W: エラーコード (下位、上位の順)
        !           966: //  +18, 1.L: ステータスコード
        !           967: //
        !           968: // 読み込みバイト数が負数ならファイルサイズを指定したものとする。
        !           969: // ステータスコードに読み込んだバイト数。
        !           970: uint32
        !           971: WindrvDevice::CmdRead(HostWindrv *host)
        !           972: {
        !           973:        uint32 dstaddr = ReadMemAddr(a5 + 14);
        !           974:        uint32 reqlen = ReadMem32(a5 + 18);
        !           975:        uint32 fcbaddr = ReadMemAddr(a5 + 22);
        !           976: 
        !           977:        if (__predict_false(loglevel >= 1)) {
        !           978:                cmdname += string_format(" Read $%06x len=$%x", fcbaddr, reqlen);
        !           979:                putlog(2, "%s", cmdname.c_str());
        !           980:        }
        !           981: 
        !           982:        FCB *fcb = SearchFCB(fcbaddr);
        !           983:        if (fcb == NULL) {
        !           984:                return Human68k::RES_INVALID_MEM; // ?
        !           985:        }
        !           986: 
        !           987:        // 負数ならファイルサイズとするらしい。
        !           988:        if ((int32)reqlen < 0) {
        !           989:                reqlen = fcb->GetSize();
        !           990:        }
        !           991: 
        !           992:        std::array<uint8, 65536> buf;
        !           993:        uint32 totallen = 0;
        !           994:        for (;;) {
        !           995:                uint32 remain = reqlen - totallen;
        !           996:                if (remain == 0) {
        !           997:                        break;
        !           998:                }
        !           999:                int len = std::min(remain, (uint32)buf.size());
        !          1000:                int n = host->Read(fcb, buf.data(), len);
        !          1001:                if (n < 0) {
        !          1002:                        return Human68k::RES_INVALID_MEM; // ?
        !          1003:                }
        !          1004:                if (n == 0) {
        !          1005:                        break;
        !          1006:                }
        !          1007:                WriteMem(dstaddr, buf.data(), n);
        !          1008:                dstaddr +=n;
        !          1009:                totallen += n;
        !          1010:        }
        !          1011: 
        !          1012:        fcb->AddPos(totallen);
        !          1013: 
        !          1014:        return totallen;
        !          1015: }
        !          1016: 
        !          1017: // $4d ファイル書き込み
        !          1018: //
        !          1019: // IN
        !          1020: //   +0, 1.B: 定数 ($1a)
        !          1021: //   +1, 1.B: ユニット番号
        !          1022: //   +2, 1.B: コマンド ($4d/$cd)
        !          1023: //     +14, 1.L: 書き込み元アドレス
        !          1024: //  +18, 1.L: 書き込みバイト数
        !          1025: //  +22, 1.L: FCB 構造体アドレス
        !          1026: // OUT
        !          1027: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1028: //  +18, 1.L: ステータスコード
        !          1029: //
        !          1030: // 書き込みバイト数が負数ならファイルサイズを指定したものとする。
        !          1031: // ステータスコードに読み込んだバイト数。
        !          1032: uint32
        !          1033: WindrvDevice::CmdWrite(HostWindrv *host)
        !          1034: {
        !          1035:        uint32 srcaddr = ReadMemAddr(a5 + 14);
        !          1036:        uint32 reqlen = ReadMem32(a5 + 18);
        !          1037:        uint32 fcbaddr = ReadMemAddr(a5 + 22);
        !          1038: 
        !          1039:        if (__predict_false(loglevel >= 1)) {
        !          1040:                cmdname += string_format(" Write $%06x len=$%x", fcbaddr, reqlen);
        !          1041:                putlog(2, "%s", cmdname.c_str());
        !          1042:        }
        !          1043: 
        !          1044:        FCB *fcb = SearchFCB(fcbaddr);
        !          1045:        if (fcb == NULL) {
        !          1046:                return Human68k::RES_INVALID_MEM; // ?
        !          1047:        }
        !          1048: 
        !          1049:        // 負数ならファイルサイズとするらしい。
        !          1050:        if ((int32)reqlen < 0) {
        !          1051:                reqlen = fcb->GetSize();
        !          1052:        }
        !          1053: 
        !          1054:        std::array<uint8, 65536> buf;
        !          1055:        uint32 totallen = 0;
        !          1056:        for (;;) {
        !          1057:                uint32 remain = reqlen - totallen;
        !          1058:                if (remain == 0) {
        !          1059:                        break;
        !          1060:                }
        !          1061:                int len = std::min(remain, (uint32)buf.size());
        !          1062:                ReadMem(buf.data(), srcaddr, len);
        !          1063:                int n = host->Write(fcb, buf.data(), len);
        !          1064:                if (n < 0) {
        !          1065:                        return Human68k::RES_INVALID_MEM; // ?
        !          1066:                }
        !          1067:                if (n == 0) {
        !          1068:                        break;
        !          1069:                }
        !          1070:                srcaddr += n;
        !          1071:                totallen += n;
        !          1072:        }
        !          1073: 
        !          1074:        fcb->AddPos(totallen);
        !          1075: 
        !          1076:        return totallen;
        !          1077: }
        !          1078: 
        !          1079: // $4e ファイルシーク
        !          1080: //
        !          1081: // IN
        !          1082: //   +0, 1.B: 定数 ($1a)
        !          1083: //   +1, 1.B: ユニット番号
        !          1084: //   +2, 1.B: コマンド ($4e/$ce)
        !          1085: //  +13, 1.B: whence (0:先頭から、1:現在位置から、2:末尾から)
        !          1086: //  +18, 1.L: オフセット
        !          1087: //  +22, 1.L: FCB 構造体アドレス
        !          1088: // OUT
        !          1089: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1090: //  +18, 1.L: ステータスコード
        !          1091: uint32
        !          1092: WindrvDevice::CmdSeek(HostWindrv *host)
        !          1093: {
        !          1094:        uint8 mode = ReadMem8(a5 + 13);
        !          1095:        int32 offset = ReadMem32(a5 + 18);
        !          1096:        uint32 fcbaddr = ReadMemAddr(a5 + 22);
        !          1097: 
        !          1098:        if (__predict_false(loglevel >= 1)) {
        !          1099:                cmdname += string_format(" Seek $%06x ($%x, %d)",
        !          1100:                        fcbaddr, offset, mode);
        !          1101:                putlog(2, "%s", cmdname.c_str());
        !          1102:        }
        !          1103: 
        !          1104:        FCB *fcb = SearchFCB(fcbaddr);
        !          1105:        if (fcb == NULL) {
        !          1106:                return Human68k::RES_INVALID_MEM; // ?
        !          1107:        }
        !          1108: 
        !          1109:        // 全世界的に同じ値だと思うけど…。
        !          1110:        int whence;
        !          1111:        switch (mode) {
        !          1112:         case 0:        whence = SEEK_SET;      break;
        !          1113:         case 1:        whence = SEEK_CUR;      break;
        !          1114:         case 2:        whence = SEEK_END;      break;
        !          1115:         default:
        !          1116:                return Human68k::RES_CANNOT_SEEK;
        !          1117:        }
        !          1118: 
        !          1119:        uint32 result = host->Seek(fcb, offset, whence);
        !          1120:        if ((int32)result < 0) {
        !          1121:                return result;
        !          1122:        }
        !          1123: 
        !          1124:        fcb->SetPos(result);
        !          1125:        return result;
        !          1126: }
        !          1127: 
        !          1128: // $4f ファイル更新時刻の取得/設定
        !          1129: //
        !          1130: // IN
        !          1131: //   +0, 1.B: 定数 ($1a)
        !          1132: //   +1, 1.B: ユニット番号
        !          1133: //   +2, 1.B: コマンド ($4f/$cf)
        !          1134: //  +18, 1.L: 日付時刻 (0 なら取得)
        !          1135: //  +22, 1.L: FCB 構造体アドレス
        !          1136: // OUT
        !          1137: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1138: //  +18, 1.L: ステータスコード
        !          1139: //
        !          1140: // 取得が成功すればステータスコードに日付時刻。
        !          1141: uint32
        !          1142: WindrvDevice::CmdFileTime(HostWindrv *host)
        !          1143: {
        !          1144:        uint32 datetime = ReadMem32(a5 + 18);
        !          1145:        uint32 fcbaddr = ReadMemAddr(a5 + 22);
        !          1146: 
        !          1147:        if (__predict_false(loglevel >= 1)) {
        !          1148:                if (datetime == 0) {
        !          1149:                        cmdname += string_format(" FileTime(Get) $%06x", fcbaddr);
        !          1150:                } else {
        !          1151:                        cmdname += string_format(" FileTime(Set) $%06x ($%08x)", fcbaddr,
        !          1152:                                datetime);
        !          1153:                }
        !          1154:                putlog(2, "%s", cmdname.c_str());
        !          1155:        }
        !          1156: 
        !          1157:        FCB *fcb = SearchFCB(fcbaddr);
        !          1158:        if (fcb == NULL) {
        !          1159:                return Human68k::RES_INVALID_MEM; // ?
        !          1160:        }
        !          1161: 
        !          1162:        uint32 result;
        !          1163:        if (datetime == 0) {
        !          1164:                result = host->GetFileTime(fcb);
        !          1165:        } else {
        !          1166:                result = host->SetFileTime(fcb, datetime);
        !          1167:        }
        !          1168:        return result;
        !          1169: }
        !          1170: 
        !          1171: // $50 GetCapacity
        !          1172: //
        !          1173: // IN
        !          1174: //   +0, 1.B: 定数 ($1a)
        !          1175: //   +1, 1.B: ユニット番号
        !          1176: //   +2, 1.B: コマンド ($50/$d0)
        !          1177: //  +14, 1.L: capacity 構造体アドレス
        !          1178: // OUT
        !          1179: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1180: //  +18, 1.L: ステータスコード
        !          1181: uint32
        !          1182: WindrvDevice::CmdGetCapacity(HostWindrv *host)
        !          1183: {
        !          1184:        if (__predict_false(loglevel >= 1)) {
        !          1185:                cmdname += " GetCapacity";
        !          1186:                putlog(2, "%s", cmdname.c_str());
        !          1187:        }
        !          1188: 
        !          1189:        uint32 capaddr = ReadMemAddr(a5 + 14);
        !          1190: 
        !          1191:        Human68k::capacity cap;
        !          1192:        uint32 result = host->GetCapacity(&cap);
        !          1193:        if ((int32)result < 0) {
        !          1194:                return result;
        !          1195:        }
        !          1196:        uint32 availbytes = cap.avail_clusters *
        !          1197:                cap.sectors_per_cluster * cap.bytes_per_sector;
        !          1198: 
        !          1199:        cap.WriteToMem(this, capaddr);
        !          1200: 
        !          1201:        return availbytes;
        !          1202: }
        !          1203: 
        !          1204: // $51 ドライブ制御/状態検査
        !          1205: //
        !          1206: // IN
        !          1207: //   +0, 1.B: 定数 ($1a)
        !          1208: //   +1, 1.B: ユニット番号
        !          1209: //   +2, 1.B: コマンド ($51/$d1)
        !          1210: //  +13, 1.B: 内部コマンド
        !          1211: //  +14, 1.L: 引数列のアドレス
        !          1212: // OUT
        !          1213: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1214: //  +13, 1.B: ドライブの状態
        !          1215: //  +18, 1.L: ステータスコード
        !          1216: //
        !          1217: // 内部コマンドは以下らしい。IOCS B_DRVCHK 参照。
        !          1218: //  0: 状態検査1
        !          1219: //  1: 排出
        !          1220: //  2: 排出禁止
        !          1221: //  3: 排出許可
        !          1222: //  4: メディア未挿入時に LED 点滅
        !          1223: //  5: メディア未挿入時に LED 消灯
        !          1224: //  6: 排出禁止2 (OS 用)
        !          1225: //  7: 排出許可2 (OS 用)
        !          1226: //  8: -
        !          1227: //  9: 状態検査2
        !          1228: //
        !          1229: // 応答のドライブの状態は以下のビットマップらしい。
        !          1230: //  0x80: LED 点灯
        !          1231: //  0x40: 排出禁止
        !          1232: //  0x20: 排出禁止2
        !          1233: //  0x10: 排出禁止1
        !          1234: //  0x08: 書き込み禁止
        !          1235: //  0x04: ドライブが準備出来ていない
        !          1236: //  0x02: メディア挿入
        !          1237: //  0x01: メディア誤挿入
        !          1238: uint32
        !          1239: WindrvDevice::CmdCtrlDrive(HostWindrv *host)
        !          1240: {
        !          1241:        uint8 ctrl = ReadMem8(a5 + 13);
        !          1242: 
        !          1243:        uint8 res;
        !          1244:        switch (ctrl) {
        !          1245:         case 0:        // 状態検査1
        !          1246:                if (__predict_false(loglevel >= 1)) {
        !          1247:                        cmdname += string_format(" CtrlDrive(%d)", ctrl);
        !          1248:                        putlog(2, "%s", cmdname.c_str());
        !          1249:                }
        !          1250:                res = 0x02;     // メディア挿入
        !          1251:                break;
        !          1252: 
        !          1253:         default:
        !          1254:                if (__predict_false(loglevel >= 1)) {
        !          1255:                        cmdname += string_format(" CtrlDrive(%d) (NOT IMPLEMENTED)", ctrl);
        !          1256:                        putlog(2, "%s", cmdname.c_str());
        !          1257:                }
        !          1258:                return -Human68k::ERR_INVALID_COMMAND_IA;
        !          1259:        }
        !          1260:        WriteMem8(a5 + 13, res);
        !          1261:        return 0;
        !          1262: }
        !          1263: 
        !          1264: // $52 DPB 取得
        !          1265: //
        !          1266: // IN
        !          1267: //   +0, 1.B: 定数 ($1a)
        !          1268: //   +1, 1.B: ユニット番号
        !          1269: //   +2, 1.B: コマンド ($52/$d2)
        !          1270: //  +14, 1.L: DPB 構造体アドレス (実際には+2 を指している)
        !          1271: // OUT
        !          1272: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1273: //  +18, 1.L: ステータスコード
        !          1274: uint32
        !          1275: WindrvDevice::CmdGetDPB(HostWindrv *host)
        !          1276: {
        !          1277:        if (__predict_false(loglevel >= 1)) {
        !          1278:                cmdname += " GetDPB";
        !          1279:                putlog(2, "%s", cmdname.c_str());
        !          1280:        }
        !          1281: 
        !          1282:        uint32 dpbaddr = ReadMemAddr(a5 + 14);
        !          1283: 
        !          1284:        Human68k::capacity cap;
        !          1285:        uint32 result = host->GetCapacity(&cap);
        !          1286:        if ((int32)result < 0) {
        !          1287:                return result;
        !          1288:        }
        !          1289: 
        !          1290:        uint shift = 31 - __builtin_clz(cap.sectors_per_cluster);
        !          1291: 
        !          1292:        // クラスタ 0: -
        !          1293:        // クラスタ 1: FAT
        !          1294:        // クラスタ 2: ルートディレクトリ
        !          1295:        // クラスタ 3: データ領域
        !          1296:        uint32 fat_sect  = cap.sectors_per_cluster * 1;
        !          1297:        uint32 root_sect = cap.sectors_per_cluster * 2;
        !          1298:        uint32 data_sect = cap.sectors_per_cluster * 3;
        !          1299: 
        !          1300:        Human68k::DPB dpb;
        !          1301:        memset(&dpb, 0, sizeof(dpb));
        !          1302:        dpb.sector_size = cap.bytes_per_sector;
        !          1303:        dpb.cluster_size = cap.sectors_per_cluster - 1;
        !          1304:        dpb.shift = shift;
        !          1305:        dpb.fat_sector = fat_sect;
        !          1306:        dpb.fat_max = 1;
        !          1307:        dpb.fat_size = cap.sectors_per_cluster;
        !          1308:        dpb.file_max = (cap.sectors_per_cluster * cap.bytes_per_sector / 32);
        !          1309:        dpb.cluster_max = cap.total_clusters;
        !          1310:        dpb.root_sector = root_sect;
        !          1311:        dpb.data_sector = data_sect;
        !          1312:        dpb.media = 0xf3;
        !          1313:        putmsg(2, "sector_size=$%x, cluster_size=$%x",
        !          1314:                dpb.sector_size, dpb.cluster_size);
        !          1315:        putmsg(2, "shift=%d, fat_sector=$%x fat_size=$%x",
        !          1316:                dpb.shift, dpb.fat_sector, dpb.fat_size);
        !          1317:        putmsg(2, "file_max=$%x cluster_max=$%x",
        !          1318:                dpb.file_max, dpb.cluster_max);
        !          1319:        putmsg(2, "root_sector=%d data_sector=%d",
        !          1320:                dpb.root_sector, dpb.data_sector);
        !          1321: 
        !          1322:        // DPB を書き戻す。
        !          1323:        // ここに渡されるアドレスは DPB+2 を指している。
        !          1324:        dpb.WriteToMem(this, dpbaddr - 2);
        !          1325: 
        !          1326:        return result;
        !          1327: }
        !          1328: 
        !          1329: uint32
        !          1330: WindrvDevice::CmdDiskRead(HostWindrv *host)
        !          1331: {
        !          1332:        cmdname += " DiskRead (NOT IMPLEMENTED)";
        !          1333:        putlog(0, "%s", cmdname.c_str());
        !          1334:        return -Human68k::ERR_INVALID_COMMAND_IA;
        !          1335: }
        !          1336: 
        !          1337: uint32
        !          1338: WindrvDevice::CmdDiskWrite(HostWindrv *host)
        !          1339: {
        !          1340:        cmdname += " DiskWrite (NOT IMPLEMENTED)";
        !          1341:        putlog(0, "%s", cmdname.c_str());
        !          1342:        return -Human68k::ERR_INVALID_COMMAND_IA;
        !          1343: }
        !          1344: 
        !          1345: uint32
        !          1346: WindrvDevice::CmdIoControl(HostWindrv *host)
        !          1347: {
        !          1348:        cmdname += " IoControl (NOT IMPLEMENTED)";
        !          1349:        putlog(0, "%s", cmdname.c_str());
        !          1350:        return -Human68k::ERR_INVALID_COMMAND_IA;
        !          1351: }
        !          1352: 
        !          1353: // $56 フラッシュ
        !          1354: //
        !          1355: // IN
        !          1356: //   +0, 1.B: 定数 ($1a)
        !          1357: //   +1, 1.B: ユニット番号
        !          1358: //   +2, 1.B: コマンド ($56/$d6)
        !          1359: // OUT
        !          1360: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1361: //  +18, 1.L: ステータスコード
        !          1362: uint32
        !          1363: WindrvDevice::CmdFlush(HostWindrv *host)
        !          1364: {
        !          1365:        if (__predict_false(loglevel >= 1)) {
        !          1366:                cmdname += " Flush";
        !          1367:                putlog(2, "%s", cmdname.c_str());
        !          1368:        }
        !          1369: 
        !          1370:        uint32 result = host->Flush();
        !          1371:        return result;
        !          1372: }
        !          1373: 
        !          1374: // $57 メディア交換チェック
        !          1375: //
        !          1376: // IN
        !          1377: //   +0, 1.B: 定数 ($1a)
        !          1378: //   +1, 1.B: ユニット番号
        !          1379: //   +2, 1.B: コマンド ($57/$d7)
        !          1380: // OUT
        !          1381: //   +3, 1.W: エラーコード (下位、上位の順)
        !          1382: //  +18, 1.L: ステータスコード
        !          1383: uint32
        !          1384: WindrvDevice::CmdCheckMedia(HostWindrv *host)
        !          1385: {
        !          1386:        // 頻繁に呼ばれるのでレベル上げておく。
        !          1387:        if (__predict_false(loglevel >= 1)) {
        !          1388:                cmdname += " CheckMedia";
        !          1389:                putlog(3, "%s", cmdname.c_str());
        !          1390:                cmdname.clear();
        !          1391:        }
        !          1392: 
        !          1393:        // メディアが有効なら 1 を返すようだ。
        !          1394:        // 現状リムーバブルメディアには対応していないので常に 1 を返す。
        !          1395:        return 1;
        !          1396: }
        !          1397: 
        !          1398: uint32
        !          1399: WindrvDevice::CmdLock(HostWindrv *host)
        !          1400: {
        !          1401:        cmdname += " Lock (NOT IMPLEMENTED)";
        !          1402:        putlog(0, "%s", cmdname.c_str());
        !          1403:        return -Human68k::ERR_INVALID_COMMAND_IA;
        !          1404: }
        !          1405: 
        !          1406: // 結果コードを処理して VM メモリに書き出す。
        !          1407: //
        !          1408: // デバイスドライバの各コマンドからの応答フォーマットには
        !          1409: // +3.W (エラーコード) と +18.L (ステータスコード) があり、
        !          1410: // 以下のように応答させるようだ。
        !          1411: //
        !          1412: // o 白帯の出るエラーの場合、
        !          1413: //   +3.W のほうにエラーコード(ERR_*)、+18.L のほうは -1(?)。
        !          1414: // o (白帯でない)エラーの場合、
        !          1415: //   +3.W のほうは 0、+18.L のほうにステータスコード。
        !          1416: // o 正常系の場合、
        !          1417: //   +3.W のほうは 0、+18.L に 0 か正の値?
        !          1418: // XXX FileTime (や Seek) のように 32bit 全域が有効な戻り値の場合
        !          1419: // どういう扱いになってるかまでは調べてない。
        !          1420: //
        !          1421: // コマンドからの戻り値を
        !          1422: // o 白帯なら -ERR_*。これには IGNORE/RETRY/ABORT のいずれかが立っているはず
        !          1423: //   なので、必ず -0x1000 より絶対値が大きい値になる。
        !          1424: // o 白帯でないエラーなら RES_*。
        !          1425: // o 正常なら 0 以上。
        !          1426: // とすることで SetResult() が +3.W と +18.L によしなに書き出す。
        !          1427: void
        !          1428: WindrvDevice::SetResult(uint32 result)
        !          1429: {
        !          1430:        // ログ表示。
        !          1431:        if (__predict_false(loglevel >= 1)) {
        !          1432:                // cmdname が空なら出力済み。
        !          1433:                if (cmdname.empty() == false) {
        !          1434:                        if (loglevel == 1) {
        !          1435:                                // ここでまとめて表示 (長い、1行で)。
        !          1436:                                putlogn("%s => %s", cmdname.c_str(), ResultStr(result).c_str());
        !          1437:                        } else {
        !          1438:                                // level 2 ならエラー時のみ表示
        !          1439:                                // level 3 なら正常時も表示
        !          1440:                                int thresh = result != 0 ? 2 : 3;
        !          1441:                                putlog(thresh, "result=%s", ResultStr(result).c_str());
        !          1442:                        }
        !          1443:                }
        !          1444:        }
        !          1445: 
        !          1446:        // 結果を格納。
        !          1447:        if ((int32)result <= -0x1000) {
        !          1448:                WriteMem8(a5 + 3, result & 0xff);
        !          1449:                WriteMem8(a5 + 4, (result >> 8) & 0xff);
        !          1450:                // ?
        !          1451:                if ((result & Human68k::ERR_RETRY) == 0) {
        !          1452:                        WriteMem32(a5 + 18, result);
        !          1453:                }
        !          1454:        } else {
        !          1455:                WriteMem32(a5 + 18, result);
        !          1456:        }
        !          1457: }
        !          1458: 
        !          1459: // result を表示用に整形して返す。
        !          1460: /*static*/ std::string
        !          1461: WindrvDevice::ResultStr(uint32 result)
        !          1462: {
        !          1463:        if (__predict_true((int32)result >= 0)) {
        !          1464:                // 正常
        !          1465:                return string_format("$%x", result);
        !          1466:        }
        !          1467: 
        !          1468:        const char *msg = Human68k::GetResultMsg(result);
        !          1469:        if (__predict_true((int32)result > -0x1000)) {
        !          1470:                // エラー応答
        !          1471:                if (__predict_true(msg)) {
        !          1472:                        return string_format("%d(%s)", (int32)result, msg);
        !          1473:                } else {
        !          1474:                        return string_format("%d", (int32)result);
        !          1475:                }
        !          1476:        } else {
        !          1477:                // 白帯
        !          1478:                if (__predict_true(msg)) {
        !          1479:                        return string_format("-$%04x(%s)", -(int32)result, msg);
        !          1480:                } else {
        !          1481:                        return string_format("-$%04x", -(int32)result);
        !          1482:                }
        !          1483:        }
        !          1484: }
        !          1485: 
        !          1486: // VM メモリから1バイト読み込む。
        !          1487: uint32
        !          1488: WindrvDevice::ReadMem8(uint32 addr) const
        !          1489: {
        !          1490:        return mainbus->HVRead8(addr);
        !          1491: }
        !          1492: 
        !          1493: // VM メモリに1バイト書き出す。
        !          1494: void
        !          1495: WindrvDevice::WriteMem8(uint32 addr, uint32 data)
        !          1496: {
        !          1497:        mainbus->HVWrite8(addr, data & 0xff);
        !          1498: }
        !          1499: 
        !          1500: // VM メモリから1ワード読み込む。
        !          1501: uint32
        !          1502: WindrvDevice::ReadMem16(uint32 addr) const
        !          1503: {
        !          1504:        return mainbus->HVRead16(addr);
        !          1505: }
        !          1506: 
        !          1507: // VM メモリに1ワード書き出す。
        !          1508: void
        !          1509: WindrvDevice::WriteMem16(uint32 addr, uint32 data)
        !          1510: {
        !          1511:        mainbus->HVWrite16(addr, data);
        !          1512: }
        !          1513: 
        !          1514: // VM メモリから1ロングワード読み込む。
        !          1515: uint32
        !          1516: WindrvDevice::ReadMem32(uint32 addr) const
        !          1517: {
        !          1518:        return mainbus->HVRead32(addr);
        !          1519: }
        !          1520: 
        !          1521: // VM メモリに1ロングワード書き出す。
        !          1522: void
        !          1523: WindrvDevice::WriteMem32(uint32 addr, uint32 data)
        !          1524: {
        !          1525:        mainbus->HVWrite32(addr, data);
        !          1526: }
        !          1527: 
        !          1528: // VM メモリからアドレスを読み込む。(下位24bitのみにする)
        !          1529: uint32
        !          1530: WindrvDevice::ReadMemAddr(uint32 addr) const
        !          1531: {
        !          1532:        uint32 data = ReadMem32(addr);
        !          1533:        return data & 0x00ffffff;
        !          1534: }
        !          1535: 
        !          1536: // VM メモリにアドレスを書き出す。(下位24bitのみにする)
        !          1537: void
        !          1538: WindrvDevice::WriteMemAddr(uint32 addr, uint32 data)
        !          1539: {
        !          1540:        data &= 0x00ffffff;
        !          1541:        WriteMem32(addr, data);
        !          1542: }
        !          1543: 
        !          1544: // VM メモリから指定の領域を読み込む。
        !          1545: // dst はホストの領域、srcaddr はゲスト内のアドレス。
        !          1546: void
        !          1547: WindrvDevice::ReadMem(void *dst, uint32 srcaddr, int len) const
        !          1548: {
        !          1549:        uint8 *d = (uint8 *)dst;
        !          1550:        for (int i = 0; i < len; i++) {
        !          1551:                *d++ = mainbus->HVRead8(srcaddr++);
        !          1552:        }
        !          1553: }
        !          1554: 
        !          1555: // VM メモリに指定の領域を書き出す。
        !          1556: // dstaddr はゲスト内のアドレス、src はホストの領域。
        !          1557: void
        !          1558: WindrvDevice::WriteMem(uint32 dstaddr, const void *src, int len)
        !          1559: {
        !          1560:        const uint8 *s = (const uint8 *)src;
        !          1561:        for (int i = 0; i < len; i++) {
        !          1562:                mainbus->HVWrite8(dstaddr++, *s++);
        !          1563:        }
        !          1564: }
        !          1565: 
        !          1566: // fname が ns の検索パターンに一致すれば true を返す。
        !          1567: bool
        !          1568: WindrvDevice::MatchFilename(const std::string& fname_, const NAMESTS& ns) const
        !          1569: {
        !          1570:        if (ns.wildcard == 21) {
        !          1571:                // "*.*" なら調べずに全部一致。
        !          1572:                return true;
        !          1573:        }
        !          1574: 
        !          1575:        std::string fname;
        !          1576:        std::string pname;
        !          1577:        if (option_case_sensitive) {
        !          1578:                fname = fname_;
        !          1579:                pname = ns.GetName();
        !          1580:        } else {
        !          1581:                fname = string_tolower(fname_);
        !          1582:                pname = string_tolower(ns.GetName());
        !          1583:        }
        !          1584: 
        !          1585:        if (ns.wildcard == 0) {
        !          1586:                // ワイルドカードなしなら単純比較。
        !          1587:                if (strcmp(fname.c_str(), pname.c_str()) == 0) {
        !          1588:                        return true;
        !          1589:                }
        !          1590:                return false;
        !          1591:        } else {
        !          1592:                return MatchWildcard(fname, pname);
        !          1593:        }
        !          1594: }
        !          1595: 
        !          1596: #endif // !TEST_WINDRV
        !          1597: 
        !          1598: // fname がパターン pname と一致すれば true を返す。
        !          1599: // pname にはワイルドカード '?' が使われている('*' は展開済み)。
        !          1600: // fname, pname の大文字小文字は必要に応じて処理済み。
        !          1601: // TODO: 漢字対応
        !          1602: /*static*/ bool
        !          1603: WindrvDevice::MatchWildcard(const std::string& fname, const std::string& pname)
        !          1604: {
        !          1605:        std::string pstr;
        !          1606:        std::string fstr;
        !          1607: 
        !          1608:        // TODO: 現状マルチピリオドには対応していないのでこれでいい。
        !          1609: 
        !          1610:        // pname、fname をそれぞれ 21文字固定形式にする。
        !          1611:        auto p = pname.find('.');
        !          1612:        if (p == std::string::npos) {
        !          1613:                pstr = string_format("%-18s%-3s", pname.c_str(), "");
        !          1614:        } else {
        !          1615:                std::string p1 = pname.substr(0, p);
        !          1616:                pstr = string_format("%-18s%-3s", p1.c_str(), &pname[p + 1]);
        !          1617:        }
        !          1618: 
        !          1619:        p = fname.find('.');
        !          1620:        if (p == std::string::npos) {
        !          1621:                fstr = string_format("%-18s%-3s", fname.c_str(), "");
        !          1622:        } else {
        !          1623:                std::string f1 = fname.substr(0, p);
        !          1624:                fstr = string_format("%-18s%-3s", f1.c_str(), &fname[p + 1]);
        !          1625:        }
        !          1626: 
        !          1627:        // 比較。
        !          1628:        for (int i = 0, iend = pstr.size(); i < iend; i++) {
        !          1629:                if (pstr[i] != '?') {
        !          1630:                        if (fstr[i] != pstr[i]) {
        !          1631:                                return false;
        !          1632:                        }
        !          1633:                }
        !          1634:        }
        !          1635:        return true;
        !          1636: }
        !          1637: 
        !          1638: #if !defined(TEST_WINDRV)
        !          1639: 
        !          1640: // addr に対応する FILES バッファを探して返す。
        !          1641: // 見付からなければ NULL を返す。
        !          1642: FilesBuf *
        !          1643: WindrvDevice::SearchFilesBuf(uint32 addr) const
        !          1644: {
        !          1645:        auto it = filesmap.find(addr);
        !          1646:        if (it == filesmap.end()) {
        !          1647:                return NULL;
        !          1648:        }
        !          1649:        return it->second;
        !          1650: }
        !          1651: 
        !          1652: // addr に対応する FILES バッファを作成して返す。
        !          1653: FilesBuf *
        !          1654: WindrvDevice::AllocFilesBuf(uint32 addr, HostWindrv *host)
        !          1655: {
        !          1656:        if (filesmap.size() >= MaxFilesBuf) {
        !          1657:                // 仕方ないので最も古いのを消すか…。
        !          1658:                auto it = std::min_element(filesmap.begin(), filesmap.end(),
        !          1659:                        [](const auto& a, const auto& b) {
        !          1660:                                return a.second->GetATime() < b.second->GetATime();
        !          1661:                        }
        !          1662:                );
        !          1663:                delete it->second;
        !          1664:                filesmap.erase(it);
        !          1665:        }
        !          1666:        putlog(2, "%s for $%08x", __func__, addr);
        !          1667:        filesmap.emplace(addr, new FilesBuf(addr, host));
        !          1668:        return filesmap[addr];
        !          1669: }
        !          1670: 
        !          1671: // addr に対応する FCB を探して返す。
        !          1672: // 見付からなければ NULL を返す。
        !          1673: Windrv::FCB *
        !          1674: WindrvDevice::SearchFCB(uint32 addr) const
        !          1675: {
        !          1676:        auto it = fcbmap.find(addr);
        !          1677:        if (it == fcbmap.end()) {
        !          1678:                return NULL;
        !          1679:        }
        !          1680:        return it->second;
        !          1681: }
        !          1682: 
        !          1683: // fcb のコピーを FCB を登録する。
        !          1684: uint32
        !          1685: WindrvDevice::AddFCB(const Windrv::FCB& fcb)
        !          1686: {
        !          1687:        assert(fcbmap.find(fcb.GetAddr()) == fcbmap.end());
        !          1688: 
        !          1689:        FCB *newfcb;
        !          1690:        try {
        !          1691:                newfcb = new FCB(fcb);
        !          1692:        } catch (...) {
        !          1693:                newfcb = NULL;
        !          1694:        }
        !          1695:        if (newfcb == NULL) {
        !          1696:                return Human68k::RES_NO_MEMORY;
        !          1697:        }
        !          1698: 
        !          1699:        fcbmap.emplace(newfcb->GetAddr(), newfcb);
        !          1700:        return 0;
        !          1701: }
        !          1702: 
        !          1703: // fcb を解放する。
        !          1704: // ホスト側のディスクリプタがオープンされていればクローズする。
        !          1705: void
        !          1706: WindrvDevice::FreeFCB(FCB *fcb)
        !          1707: {
        !          1708:        assert(fcb);
        !          1709: 
        !          1710:        fcb->Close();
        !          1711:        auto it = fcbmap.find(fcb->GetAddr());
        !          1712:        if (it != fcbmap.end()) {
        !          1713:                fcbmap.erase(it);
        !          1714:        }
        !          1715: }
        !          1716: 
        !          1717: 
        !          1718: //
        !          1719: // FilesBuf
        !          1720: //
        !          1721: 
        !          1722: // コンストラクタ
        !          1723: FilesBuf::FilesBuf(uint32 key_, HostWindrv *host_)
        !          1724: {
        !          1725:        Init(key_, host_);
        !          1726: }
        !          1727: 
        !          1728: // 初期化
        !          1729: void
        !          1730: FilesBuf::Init(uint32 key_, HostWindrv *host_)
        !          1731: {
        !          1732:        key = key_;
        !          1733:        host = host_;
        !          1734:        path.clear();
        !          1735:        list.clear();
        !          1736:        UpdateATime();
        !          1737: }
        !          1738: 
        !          1739: // 参照時刻を更新。
        !          1740: void
        !          1741: FilesBuf::UpdateATime()
        !          1742: {
        !          1743:        struct timeval tv;
        !          1744: 
        !          1745:        gettimeofday(&tv, NULL);
        !          1746:        atime = (uint64)tv.tv_sec * 1000'000 + (uint64)tv.tv_usec;
        !          1747: }
        !          1748: 
        !          1749: 
        !          1750: //
        !          1751: // Human68k
        !          1752: //
        !          1753: 
        !          1754: // 属性 attr をデバッグ表示用文字列にして返す。
        !          1755: /*static*/ std::string
        !          1756: Human68k::AttrStr(uint8 attr)
        !          1757: {
        !          1758:        static const char attrchr[6] = {
        !          1759:                'A',    // Archive
        !          1760:                'D',    // Directory
        !          1761:                'V',    // Volume
        !          1762:                'S',    // System
        !          1763:                'H',    // Hidden
        !          1764:                'R',    // ReadOnly
        !          1765:        };
        !          1766:        char buf[8 + 1];
        !          1767:        char *d = buf;
        !          1768: 
        !          1769:        // bit 7,6 はサポートしていないので立ってる時だけ表示。
        !          1770:        if (__predict_false((attr & 0xc0) != 0)) {
        !          1771:                *d++ = (attr & 0x80) ? '7' : '-';
        !          1772:                *d++ = (attr & 0x40) ? '6' : '-';
        !          1773:        }
        !          1774:        uint8 mask = 0x20;
        !          1775:        for (int i = 0; i < 6; i++) {
        !          1776:                *d++ = (attr & mask) ? attrchr[i] : '-';
        !          1777:                mask >>= 1;
        !          1778:        }
        !          1779:        *d = '\0';
        !          1780:        return std::string(buf);
        !          1781: }
        !          1782: 
        !          1783: // オープンモードを表示用文字列にする。
        !          1784: /*static*/ std::string
        !          1785: Human68k::OpenModeStr(uint8 openmode)
        !          1786: {
        !          1787:        switch (openmode) {
        !          1788:         case OPEN_RDONLY:      return "O_RDONLY";
        !          1789:         case OPEN_WRONLY:      return "O_WRONLY";
        !          1790:         case OPEN_RDWR:        return "O_RDWR";
        !          1791:         default:
        !          1792:                return string_format("$%02x", openmode);
        !          1793:        }
        !          1794: }
        !          1795: 
        !          1796: /*static*/ const char * const
        !          1797: Human68k::result_msg[] =
        !          1798: {
        !          1799:        "File exists",                                                  // -80 RES_FILE_EXIST
        !          1800: 
        !          1801:        "Invalid Function",                                             //  -1 RES_INVALID_FUNC
        !          1802:        "File not found",                                               //  -2 RES_FILE_NOT_FOUND
        !          1803:        "Directory not found",                                  //  -3 RES_DIR_NOT_FOUND
        !          1804:        "Too many opened handles",                              //  -4 RES_TOO_MANY_HANDLE
        !          1805:        "Directory or volume",                                  //  -5 RES_NOT_A_FILE
        !          1806:        "Handle not opened",                                    //  -6 RES_NOT_OPENED
        !          1807:        "Broken memory",                                                //  -7 RES_INVALID_MEM
        !          1808:        "Out of memory",                                                //  -8 RES_NO_MEMORY
        !          1809:        "Invalid pointer",                                              //  -9 RES_INVALID_PTR
        !          1810:        "Invalid environment",                                  // -10 RES_INVALID_ENV
        !          1811:        "Illegal executable format",                    // -11 RES_ILLEGAL_FORMAT
        !          1812:        "Invalid open mode",                                    // -12 RES_INVALID_MODE
        !          1813:        "Invalid path",                                                 // -13 RES_INVALID_PATH
        !          1814:        "Invalid parameter",                                    // -14 RES_INVALID_PARAM
        !          1815:        "Invalid drive",                                                // -15 RES_INVALID_DRIVE
        !          1816:        "Current directory cannot be removed",  // -16 RES_CURRENT_DIR
        !          1817:        "No ioctrl for this device",                    // -17 RES_NO_IOCTRL
        !          1818:        "No more files found",                                  // -18 RES_LAST_FILE
        !          1819:        "Cannot Write a file",                                  // -19 RES_CANNOT_WRITE
        !          1820:        "Directory exists",                                             // -20 RES_DIR_EXISTS
        !          1821:        "Cannot delete because file exists",    // -21 RES_CANNOT_DELETE
        !          1822:        "Cannot rename because file exists",    // -22 RES_CANNOT_RENAME
        !          1823:        "Disk full",                                                    // -23 RES_DISK_FULL
        !          1824:        "Directory entry full",                                 // -24 RES_DIR_FULL
        !          1825:        "Cannot Seek",                                                  // -25 RES_CANNOT_SEEK
        !          1826:        "Supervisor called again",                              // -26 RES_SUPERVISOR
        !          1827:        "Thread name exists",                                   // -27 RES_THREAD_EXISTS
        !          1828:        "Inter-process buffer write-protected", // -28 RES_PROCESS_BUF
        !          1829:        "Cannot execute background process",    // -29 RES_BACKGROUND
        !          1830:        NULL,   // -30
        !          1831:        NULL,   // -31
        !          1832:        "Lock buffer full",                                             // -32 RES_NO_LOCKBUF
        !          1833:        "Locked",                                                               // -33 RES_LOCKED
        !          1834:        "Drive handle opened",                                  // -34 RES_DRIVE_OPENED
        !          1835:        "Too many links",                                               // -35 RES_TOO_MANY_LINKS
        !          1836: };
        !          1837: 
        !          1838: /*static*/ const char * const
        !          1839: Human68k::err_msg[] =
        !          1840: {
        !          1841:        NULL,
        !          1842:        "Invalid unit number",          // $0001 ERR_INVALID_UNIT
        !          1843:        NULL,                                           // $0002
        !          1844:        "Invalid command",                      // $0003 ERR_INVALID_COMMAND
        !          1845:        NULL,                                           // $0004
        !          1846:        NULL,                                           // $0005
        !          1847:        NULL,                                           // $0006
        !          1848:        NULL,                                           // $0007
        !          1849:        NULL,                                           // $0008
        !          1850:        NULL,                                           // $0009
        !          1851:        "Write error",                          // $000a ERR_WRITE
        !          1852:        "Read error",                           // $000b ERR_READ
        !          1853:        "Unknown error",                        // $000c ERR_MISC
        !          1854:        NULL,                                           // $000d
        !          1855:        "Write protected",                      // $000e ERR_WRITE_PROTECT
        !          1856: };
        !          1857: 
        !          1858: // ステータスコードまたはエラーコード result に対応するメッセージを返す。
        !          1859: // なければ NULL を返す。
        !          1860: // result は負数を渡すこと。
        !          1861: // result がエラーコードの場合 Ignore/Retry/Abort ビットの差異は無視する。
        !          1862: /*static*/ const char *
        !          1863: Human68k::GetResultMsg(uint32 result)
        !          1864: {
        !          1865:        assert((int32)result < 0);
        !          1866: 
        !          1867:        result = -(int32)result;
        !          1868:        if (result < 0x1000) {
        !          1869:                // これ一つだけ遠いのでリマップする
        !          1870:                if (__predict_false(result == 80)) {
        !          1871:                        result = 0;
        !          1872:                }
        !          1873:                if (__predict_true(result < countof(result_msg))) {
        !          1874:                        return result_msg[result];
        !          1875:                }
        !          1876:        } else {
        !          1877:                result &= 0xfff;
        !          1878: 
        !          1879:                if (__predict_true(result < countof(err_msg))) {
        !          1880:                        return err_msg[result];
        !          1881:                }
        !          1882:        }
        !          1883:        return NULL;
        !          1884: }
        !          1885: 
        !          1886: #endif // !TEST_WINDRV
        !          1887: 
        !          1888: // Unix 時刻を MS-DOS 形式の日付時刻に変換する。
        !          1889: /*static*/ uint32
        !          1890: Human68k::Unixtime2DateTime(time_t t)
        !          1891: {
        !          1892:        struct tm *tm = localtime(&t);
        !          1893:        int year = tm->tm_year + 1900;
        !          1894:        int mon  = tm->tm_mon + 1;
        !          1895:        int day  = tm->tm_mday;
        !          1896:        int hour = tm->tm_hour;
        !          1897:        int min  = tm->tm_min;
        !          1898:        int sec  = tm->tm_sec;
        !          1899: 
        !          1900:        if (year < 1980) {
        !          1901:                year = 1980;
        !          1902:                mon  = 1;
        !          1903:                day  = 1;
        !          1904:                hour = 0;
        !          1905:                min  = 0;
        !          1906:                sec  = 0;
        !          1907:        }
        !          1908: 
        !          1909:        uint32 datetime =
        !          1910:                  ((year - 1980) << 25)
        !          1911:                | (mon << 21)
        !          1912:                | (day << 16)
        !          1913:                | (hour << 11)
        !          1914:                | (min << 5)
        !          1915:                | (sec / 2);
        !          1916:        return datetime;
        !          1917: }
        !          1918: 
        !          1919: // MS-DOS 形式の日付時刻を Unix 時刻に変換する。
        !          1920: /*static*/ time_t
        !          1921: Human68k::DateTime2Unixtime(uint32 datetime)
        !          1922: {
        !          1923:        int year = (datetime >> 25) + 1980;
        !          1924:        int mon  = (datetime >> 21) & 0xf;
        !          1925:        int mday = (datetime >> 16) & 0x1f;
        !          1926:        int hour = (datetime >> 11) & 0x1f;
        !          1927:        int min  = (datetime >>  5) & 0x3f;
        !          1928:        int sec  = (datetime & 0x1f) << 1;
        !          1929: 
        !          1930:        struct tm tm {};
        !          1931:        tm.tm_year = year - 1900;
        !          1932:        tm.tm_mon  = mon - 1;
        !          1933:        tm.tm_mday = mday;
        !          1934:        tm.tm_hour = hour;
        !          1935:        tm.tm_min  = min;
        !          1936:        tm.tm_sec  = sec;
        !          1937:        return mktime(&tm);
        !          1938: }
        !          1939: 
        !          1940: #if !defined(TEST_WINDRV)
        !          1941: 
        !          1942: // この FILES を (parent を通じて) VM メモリに書き出す。
        !          1943: // Files/NFiles で書き戻す必要のあるフィールドのみ。
        !          1944: void
        !          1945: Human68k::FILES::WriteToMem(WindrvDevice *parent, uint32 addr) const
        !          1946: {
        !          1947:        //parent->WriteMem8 (addr + 0x00, sattr);
        !          1948:        //parent->WriteMem8 (addr + 0x01, drive);
        !          1949:        parent->WriteMem32(addr + 0x02, sector);
        !          1950:        parent->WriteMem16(addr + 0x08, offset);
        !          1951:        parent->WriteMem8 (addr + 0x15, attr);
        !          1952:        parent->WriteMem16(addr + 0x16, time);
        !          1953:        parent->WriteMem16(addr + 0x18, date);
        !          1954:        parent->WriteMem32(addr + 0x1a, size);
        !          1955: 
        !          1956:        std::array<char, 23> namebuf {};
        !          1957:        strlcpy(namebuf.data(), name.c_str(), namebuf.size());
        !          1958:        parent->WriteMem(addr + 0x1e, namebuf.data(), namebuf.size());
        !          1959: }
        !          1960: 
        !          1961: // この capacity を (parent を通じて) VM メモリに書き出す。
        !          1962: void
        !          1963: Human68k::capacity::WriteToMem(WindrvDevice *parent, uint32 addr) const
        !          1964: {
        !          1965:        parent->WriteMem16(addr + 0, avail_clusters);
        !          1966:        parent->WriteMem16(addr + 2, total_clusters);
        !          1967:        parent->WriteMem16(addr + 4, sectors_per_cluster);
        !          1968:        parent->WriteMem16(addr + 6, bytes_per_sector);
        !          1969: }
        !          1970: 
        !          1971: // この DPB を (parent を通じて) VM メモリに書き出す。
        !          1972: void
        !          1973: Human68k::DPB::WriteToMem(WindrvDevice *parent, uint32 addr) const
        !          1974: {
        !          1975:        parent->WriteMem16(addr +  2, sector_size);
        !          1976:        parent->WriteMem8 (addr +  4, cluster_size);
        !          1977:        parent->WriteMem8 (addr +  5, shift);
        !          1978:        parent->WriteMem16(addr +  6, fat_sector);
        !          1979:        parent->WriteMem8 (addr +  8, fat_max);
        !          1980:        parent->WriteMem8 (addr +  9, fat_size);
        !          1981:        parent->WriteMem16(addr + 10, file_max);
        !          1982:        parent->WriteMem16(addr + 12, data_sector);
        !          1983:        parent->WriteMem16(addr + 14, cluster_max);
        !          1984:        parent->WriteMem16(addr + 16, root_sector);
        !          1985:        parent->WriteMem8 (addr + 20, media);
        !          1986: }
        !          1987: 
        !          1988: 
        !          1989: //
        !          1990: // FCB
        !          1991: //
        !          1992: 
        !          1993: // コンストラクタ (Human68k のほう)
        !          1994: Human68k::FCB::FCB(WindrvDevice *parent_, uint32 addr_)
        !          1995: {
        !          1996:        parent = parent_;
        !          1997:        addr = addr_;
        !          1998: }
        !          1999: 
        !          2000: uint32
        !          2001: Human68k::FCB::GetPos() const
        !          2002: {
        !          2003:        return parent->ReadMem32(addr + 0x06);
        !          2004: }
        !          2005: 
        !          2006: void
        !          2007: Human68k::FCB::SetPos(uint32 pos)
        !          2008: {
        !          2009:        parent->WriteMem32(addr + 0x06, pos);
        !          2010: }
        !          2011: 
        !          2012: void
        !          2013: Human68k::FCB::AddPos(uint32 diff)
        !          2014: {
        !          2015:        uint32 pos = GetPos();
        !          2016:        SetPos(pos + diff);
        !          2017: }
        !          2018: 
        !          2019: uint8
        !          2020: Human68k::FCB::GetMode() const
        !          2021: {
        !          2022:        return parent->ReadMem8(addr + 0x0e);
        !          2023: }
        !          2024: 
        !          2025: void
        !          2026: Human68k::FCB::SetDateTime(uint32 datetime)
        !          2027: {
        !          2028:        uint32 date = datetime >> 16;
        !          2029:        uint32 time = datetime & 0xffff;
        !          2030:        parent->WriteMem16(addr + 0x3a, time);
        !          2031:        parent->WriteMem16(addr + 0x3c, date);
        !          2032: }
        !          2033: 
        !          2034: uint32
        !          2035: Human68k::FCB::GetSize() const
        !          2036: {
        !          2037:        return parent->ReadMem32(addr + 0x40);
        !          2038: }
        !          2039: 
        !          2040: void
        !          2041: Human68k::FCB::SetSize(uint32 size)
        !          2042: {
        !          2043:        parent->WriteMem32(addr + 0x40, size);
        !          2044: }
        !          2045: 
        !          2046: void
        !          2047: Human68k::FCB::SetName(const std::string& name1, const std::string& name2,
        !          2048:        const std::string& ext)
        !          2049: {
        !          2050:        std::array<char, 8 + 10> namebuf;
        !          2051:        std::array<char, 3>  extbuf;
        !          2052: 
        !          2053:        // name1
        !          2054:        int i = 0;
        !          2055:        for (auto c : name1) {
        !          2056:                namebuf[i++] = c;
        !          2057:        }
        !          2058:        while (i < 8) {
        !          2059:                namebuf[i++] = ' ';
        !          2060:        }
        !          2061: 
        !          2062:        // name2
        !          2063:        for (auto c : name2) {
        !          2064:                namebuf[i++] = c;
        !          2065:        }
        !          2066:        while (i < 8 + 10) {
        !          2067:                namebuf[i++] = '\0';
        !          2068:        }
        !          2069: 
        !          2070:        // ext
        !          2071:        i = 0;
        !          2072:        for (auto c : ext) {
        !          2073:                extbuf[i++] = c;
        !          2074:        }
        !          2075:        while (i < 3) {
        !          2076:                extbuf[i++] = ' ';
        !          2077:        }
        !          2078: 
        !          2079:        parent->WriteMem(addr + 0x24, namebuf.data(), 8);
        !          2080:        parent->WriteMem(addr + 0x30, namebuf.data() + 8, 10);
        !          2081:        parent->WriteMem(addr + 0x2c, extbuf.data(), extbuf.size());
        !          2082: }
        !          2083: 
        !          2084: // コンストラクタ (Windrv のほう)
        !          2085: Windrv::FCB::FCB(WindrvDevice *parent, uint32 addr, HostWindrv *host_)
        !          2086:        : inherited(parent, addr)
        !          2087: {
        !          2088:        host = host_;
        !          2089: }
        !          2090: 
        !          2091: // FCB をクローズする。
        !          2092: // CmdOpen() の構造上デストラクタではクローズしないので明示的に呼ぶこと。
        !          2093: void
        !          2094: Windrv::FCB::Close()
        !          2095: {
        !          2096:        if (host) {
        !          2097:                host->Close(this);
        !          2098:        }
        !          2099: }
        !          2100: 
        !          2101: 
        !          2102: //
        !          2103: // NAMESTS
        !          2104: //
        !          2105: 
        !          2106: // コンストラクタ
        !          2107: WindrvDevice::NAMESTS::NAMESTS(WindrvDevice *windrv, uint32 addr)
        !          2108: {
        !          2109:        std::array<uint8, Size> data;
        !          2110:        int i;
        !          2111: 
        !          2112:        windrv->ReadMem(data.data(), addr, data.size());
        !          2113: 
        !          2114:        // メンバをセット。
        !          2115:        wildcard = data[0];
        !          2116:        drive = data[1];
        !          2117: 
        !          2118:        // パスは '\\' らしいけど '\t' が来るようだ。何故?
        !          2119:        // どちらにしてもここでは '/' にする。
        !          2120:        for (i = 0; i < 65; i++) {
        !          2121:                uint8 c = data[2 + i];
        !          2122:                if (c == '\0') {
        !          2123:                        break;
        !          2124:                }
        !          2125:                if (c == '\\' || c == '\t') {
        !          2126:                        c = '/';
        !          2127:                }
        !          2128:                path += c;
        !          2129:        }
        !          2130: 
        !          2131:        // name1, ext は ' ' でパディングされているので取り除く。
        !          2132:        for (i = 0; i < 8; i++) {
        !          2133:                uint8 c = data[0x43 + i];
        !          2134:                if (c == ' ') {
        !          2135:                        break;
        !          2136:                }
        !          2137:                name1 += c;
        !          2138:        }
        !          2139: 
        !          2140:        for (i = 0; i < 3; i++) {
        !          2141:                uint8 c = data[0x4b + i];
        !          2142:                if (c == ' ') {
        !          2143:                        break;
        !          2144:                }
        !          2145:                ext += c;
        !          2146:        }
        !          2147: 
        !          2148:        // name2 は '\0' でパディングされている。
        !          2149:        for (i = 0; i < 10; i++) {
        !          2150:                uint8 c = data[0x4e + i];
        !          2151:                if (c == '\0') {
        !          2152:                        break;
        !          2153:                }
        !          2154:                name2 += c;
        !          2155:        }
        !          2156: }
        !          2157: 
        !          2158: // ドライブ:パスを返す。
        !          2159: std::string
        !          2160: WindrvDevice::NAMESTS::GetDrivePath() const
        !          2161: {
        !          2162:        std::string buf;
        !          2163: 
        !          2164:        buf = 'A' + drive;
        !          2165:        buf += ':';
        !          2166:        buf += GetPath();
        !          2167:        return buf;
        !          2168: }
        !          2169: 
        !          2170: // ファイル名文字列を返す。
        !          2171: std::string
        !          2172: WindrvDevice::NAMESTS::GetName() const
        !          2173: {
        !          2174:        std::string buf;
        !          2175: 
        !          2176:        buf = name1 + name2;
        !          2177:        if (ext.empty() == false) {
        !          2178:                buf += '.';
        !          2179:                buf += ext;
        !          2180:        }
        !          2181:        return buf;
        !          2182: }
        !          2183: 
        !          2184: // ドライブ:パス+ファイル名を返す。
        !          2185: std::string
        !          2186: WindrvDevice::NAMESTS::GetFull() const
        !          2187: {
        !          2188:        return GetDrivePath() + GetName();
        !          2189: }
        !          2190: 
        !          2191: // ドライブとパス名のログ表示用文字列を返す。
        !          2192: // "D:'/path/'"
        !          2193: std::string
        !          2194: WindrvDevice::NAMESTS::PrintDrivePath() const
        !          2195: {
        !          2196:        std::string buf;
        !          2197: 
        !          2198:        buf = 'A' + drive;
        !          2199:        buf += ":'";
        !          2200:        buf += GetPath();
        !          2201:        buf += '\'';
        !          2202:        return buf;
        !          2203: }
        !          2204: 
        !          2205: 
        !          2206: //
        !          2207: // VDir
        !          2208: //
        !          2209: 
        !          2210: // コンストラクタ
        !          2211: VDir::VDir()
        !          2212: {
        !          2213: }
        !          2214: 
        !          2215: // コンストラクタ
        !          2216: VDir::VDir(HostWindrv *host_, const std::string& path_)
        !          2217: {
        !          2218:        host = host_;
        !          2219:        path = path_;
        !          2220: }
        !          2221: 
        !          2222: // このディレクトリを使用開始する。
        !          2223: void
        !          2224: VDir::Acquire()
        !          2225: {
        !          2226:        refcount++;
        !          2227: }
        !          2228: 
        !          2229: // このディレクトリを使用終了する。
        !          2230: void
        !          2231: VDir::Release()
        !          2232: {
        !          2233:        if (__predict_true(refcount > 0)) {
        !          2234:                refcount--;
        !          2235:        } else {
        !          2236:                host->putlogf(0, lstr("%s %s: refcount=%d",
        !          2237:                        __func__, path.c_str(), refcount));
        !          2238:        }
        !          2239: }
        !          2240: 
        !          2241: // name にマッチするエントリを返す。
        !          2242: // want_exist == true なら(オープンや削除のために)存在する1つを見付けたい方。
        !          2243: // want_exist == false なら(作成する前とかに)存在しないことを確認したい方。
        !          2244: //
        !          2245: // 大文字小文字を区別しないモードで、大文字と小文字の同じファイル名が
        !          2246: // 複数あった場合、true なら見付かったが1つに定まらないので NULL を返す。
        !          2247: // false ならいずれにしろ存在するのでどれか1つを返す。
        !          2248: const VDirent *
        !          2249: VDir::MatchName(const std::string& name, bool want_exist) const
        !          2250: {
        !          2251:        if (Windrv::option_case_sensitive) {
        !          2252:                // 大文字小文字を区別する場合。
        !          2253:                for (const auto& e : list) {
        !          2254:                        if (e.name == name) {
        !          2255:                                return &e;
        !          2256:                        }
        !          2257:                }
        !          2258:                return NULL;
        !          2259:        } else {
        !          2260:                // 大文字小文字を区別しない場合。
        !          2261:                const VDirent *found = NULL;
        !          2262:                int n = 0;
        !          2263:                for (const auto& e : list) {
        !          2264:                        if (strcasecmp(e.name.c_str(), name.c_str()) == 0) {
        !          2265:                                if (n++ == 0) {
        !          2266:                                        found = &e;
        !          2267:                                }
        !          2268:                        }
        !          2269:                }
        !          2270:                // 1つ以下なら確定。
        !          2271:                if (n <= 1) {
        !          2272:                        return found;
        !          2273:                }
        !          2274:                // 2つ以上なら、
        !          2275:                if (want_exist) {
        !          2276:                        return NULL;
        !          2277:                } else {
        !          2278:                        return found;
        !          2279:                }
        !          2280:        }
        !          2281: }
        !          2282: 
        !          2283: // 参照時刻を更新する。
        !          2284: void
        !          2285: VDir::UpdateATime()
        !          2286: {
        !          2287:        struct timeval tv;
        !          2288: 
        !          2289:        gettimeofday(&tv, NULL);
        !          2290:        atime = (uint64)tv.tv_sec * 1000'000 + (uint64)tv.tv_usec;
        !          2291: }
        !          2292: 
        !          2293: 
        !          2294: //
        !          2295: // AutoVDir
        !          2296: //
        !          2297: 
        !          2298: // デストラクタ
        !          2299: AutoVDir::~AutoVDir()
        !          2300: {
        !          2301:        if (host && vdir) {
        !          2302:                host->CloseVDir(vdir);
        !          2303:        }
        !          2304: }
        !          2305: 
        !          2306: 
        !          2307: //
        !          2308: // VDirent
        !          2309: //
        !          2310: 
        !          2311: // コンストラクタ
        !          2312: VDirent::VDirent(const std::string& name_, bool isdir_)
        !          2313:        : name(name_), isdir(isdir_), isvalid(true)
        !          2314: {
        !          2315: }
        !          2316: 
        !          2317: #endif // !TEST_WINDRV

unix.superglobalmegacorp.com

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