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