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