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