Annotation of nono/vm/windrv.cpp, revision 1.1.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.