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