|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2022 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // FDD
9: //
10:
11: // ディスクメディアは 77~ 個のシリンダで構成される。
12: // シリンダは両面の場合 2 本のトラックで構成される。
13: // トラックは 8~ 個のセクタで構成される。
14: // セクタは 128~ バイトで構成される。
15:
1.1.1.3 root 16: // 割り込み状態はこちらでは保持しておらず PEDEC 側で持っている。
1.1.1.5 root 17: // PEDEC への割り込みは AssertINT() するだけで使っている。
1.1.1.3 root 18:
1.1 root 19: #include "fdd.h"
20: #include "config.h"
1.1.1.6 root 21: #include "event.h"
1.1 root 22: #include "fdc.h"
23: #include "mainapp.h"
24: #include "pedec.h"
25: #include "scheduler.h"
26: #include "uimessage.h"
27:
28: #define MFM_TRACK_HEADER (80 + 12 + 3 + 1 + 50)
29: #define MFM_SECTOR_HEADER (12 + 3 + 1 + 4 + 2 + 22 + 12 + 3 + 1 + 2)
30: #define MFM_TRACK_RAW_SIZE (10416)
31:
1.1.1.2 root 32: //
33: // フロッピーディスクの 1 トラック
34: //
35:
36: // トラックデータの記録開始
37: void
38: FDTrack::Begin()
39: {
40: markpos.push_back(buf.size());
41: }
42:
43: // トラックデータの記録終了
44: void
45: FDTrack::End()
46: {
47: assert(buf.size() == MFM_TRACK_RAW_SIZE);
48:
49: markpos.push_back(buf.size());
50: }
51:
52: // トラックデータに data を追加
1.1 root 53: void
54: FDTrack::Append(uint8 data)
55: {
56: buf.push_back(data);
57: }
58:
1.1.1.2 root 59: // トラックデータに data を count 個追加
1.1 root 60: void
61: FDTrack::Append(uint8 data, int count)
62: {
63: for (int i = 0; i < count; i++) {
64: Append(data);
65: }
66: }
67:
68: // トラックデータにマークを追加
69: void
70: FDTrack::AppendMark(uint8 data)
71: {
72: // マーク位置を記録
73: markpos.push_back(buf.size());
74: Append(data);
75: }
76:
77: void
78: FDTrack::Clear()
79: {
80: buf.clear();
81: markpos.clear();
82: }
83:
84:
1.1.1.2 root 85: //
86: // フロッピードライブ
87: //
1.1 root 88:
1.1.1.2 root 89: // コンストラクタ
1.1.1.4 root 90: FDDDevice::FDDDevice(uint unit_)
1.1.1.2 root 91: : inherited(OBJ_FDD(unit_))
1.1 root 92: {
93: unit = unit_;
1.1.1.7 ! root 94:
! 95: // image はコンストラクタがないので、この辺りで設定。
! 96: image.SetName(GetName());
1.1 root 97: }
98:
1.1.1.2 root 99: // デストラクタ
1.1 root 100: FDDDevice::~FDDDevice()
101: {
102: }
103:
104: // 初期化
105: bool
106: FDDDevice::Init()
107: {
1.1.1.2 root 108: fdc = GetFDCDevice();
109: pedec = GetPEDECDevice();
110:
1.1.1.4 root 111: const std::string keybody = string_format("fd%u-", unit);
1.1 root 112: const std::string imgkey = keybody + "image";
113: const std::string wikey = keybody + "writeignore";
114:
115: const ConfigItem& itemimg = gConfig->Find(imgkey);
116: const std::string& filename = itemimg.AsString();
117:
118: // UI からの通知を受け取るイベントを用意
1.1.1.2 root 119: scheduler->ConnectMessage(MessageID::FDD_LOAD(unit), this,
1.1.1.3 root 120: ToMessageCallback(&FDDDevice::LoadMessage));
1.1.1.2 root 121: scheduler->ConnectMessage(MessageID::FDD_UNLOAD(unit), this,
1.1.1.3 root 122: ToMessageCallback(&FDDDevice::UnloadMessage));
1.1 root 123:
1.1.1.6 root 124: auto evman = GetEventManager();
125: event = evman->Regist(this,
126: ToEventCallback(&FDDDevice::EventCallback),
127: GetName());
1.1.1.4 root 128:
1.1 root 129: // オープン前に writeignore をチェック。
130: // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
131: // 行わないといけない。うーん。
1.1.1.7 ! root 132: write_ignore = gConfig->Find(wikey).AsBool();
1.1 root 133:
134: // 設定時点でファイルが指定されていればオープン
135: if (filename.empty() == false) {
1.1.1.3 root 136: std::string path = gMainApp.NormalizePath(filename);
1.1 root 137: if (LoadDisk(path) == false) {
1.1.1.7 ! root 138: itemimg.Err("Could not open %s", path.c_str());
1.1 root 139: return false;
140: }
141: }
142:
143: return true;
144: }
145:
146: // リセット
147: void
148: FDDDevice::ResetHard(bool poweron)
149: {
150: if (poweron) {
151: motor_state = STOPPED;
152: }
153:
154: // XXX どこでやる?
155: eject_enable = true;
156: }
157:
158: // モニタの下請け (fdc から呼ばれる)。
159: // y = 11 以降が各ドライブの状態で、このうち x = 26 以降が FDD の担当。
160: // y = 16 以降は FDD のアクセスマップ。
161: void
1.1.1.7 ! root 162: FDDDevice::MonitorScreenFDD(TextScreen& screen, uint hd)
1.1 root 163: {
164: /* 1 2 3 4 5 6 7
165: 0123456789012345678901234567890123456789012345678901234567890123456789012345678
166: ready accled eject fmt C H R N
167: stop blink disable 2HC 99 1 99 2(1024)
168: Unit0: HD=1 cyl=nn/nn pos=12345/12345
169: XX>01234567890123456 01234567890123456| XX>01234567890123456 01234567890123456|
170: */
171:
172: int uy = 11 + unit;
173: int ux = 26;
174:
175: // モータ状態
176: const char * const motor_state_str[] = {
177: "Stop",
178: "Down",
179: "Up",
180: "Ready",
181: };
182: screen.Print(ux, uy, "%s", motor_state_str[(int)motor_state]);
183:
184: // アクセス LED 状態
185: const char * const access_led_str[] = {
186: "Off",
187: "Green",
188: "Red",
189: "Blink",
190: };
191: screen.Print(ux + 6, uy, "%s", access_led_str[(int)access_led]);
192:
193: // イジェクト可否 (LED 状態)
194: const char *eject_str;
195: if (medium_loaded) {
196: if (eject_enable) {
197: eject_str = "Enable";
198: } else {
199: eject_str = "Disable";
200: }
201: } else {
202: eject_str = "Off";
203: }
204: screen.Puts(ux + 13, uy, eject_str);
205:
206: if (medium_loaded) {
207: // フォーマットと CHRN
1.1.1.4 root 208: screen.Print(ux + 20, uy, "%4s %2u %u %2u %u(%u)",
1.1 root 209: format_name.c_str(), ncyl, nhead, nsect, format_n, sectsize);
210: }
211:
212: uy = 16 + (unit / 2) * 10;
213: ux = (unit % 2) * 41;
214:
1.1.1.4 root 215: screen.Print(ux, uy++, "Unit %u", unit);
1.1 root 216: // 横ガイド
217: for (int i = 0; i < 8; i++) {
218: screen.Puts(ux + 2, uy + i, "|");
219: screen.Puts(ux + 38, uy + i, "|");
220: }
221:
222: // メディアが入ってなければここで終わり
223: if (IsMediumLoaded() == false) {
224: return;
225: }
226:
227: TA selattr = selected ? TA::Em : TA::Normal;
1.1.1.4 root 228: screen.Print(ux, uy - 1, selattr, "Unit %u: HD=%u cyl=%2u/%u pos=%5u/%u",
1.1 root 229: unit, hd, cylinder, ncyl, pos, MFM_TRACK_RAW_SIZE);
230:
231: // ヘッドの現在位置。
232: // 横は全長を当分割、縦は1行で10トラック固定にする
233: int headx = (double)pos / MFM_TRACK_RAW_SIZE * 17;
234: int heady = (double)cylinder / 10;
235:
236: // 片面ディスクはサポートしていないが一応
237: for (int h = 0; h < nhead; h++) {
238: for (int i = 0; i < 8; i++) {
239: TA trkattr = (__predict_false(selected && i == heady && h == hd))
240: ? TA::Em : TA::Normal;
241: screen.Puts(ux + h * 18 + 3, uy + i, trkattr,
242: // 01234567890123456
243: ".................");
244: }
245: }
246:
247: // ヘッド位置
1.1.1.4 root 248: screen.Print(ux, uy + heady, selattr, "%2u>", cylinder);
1.1 root 249: screen.Puts(ux + hd * 18 + 3 + headx, uy + heady, selattr, "#");
250: }
251:
252: // READY 信号
253: bool
254: FDDDevice::GetReady() const
255: {
256: return (motor_state == READY);
257: }
258:
259: // TRACK00 信号
260: bool
261: FDDDevice::GetTrack0() const
262: {
263: return (cylinder == 0);
264: }
265:
266: bool
267: FDDDevice::GetWriteProtect() const
268: {
269: return write_mode == RW::WriteProtect;
270: }
271:
272: void
273: FDDDevice::DoSeek(int dir)
274: {
275: if ((dir >= 0 && cylinder < 80) || (dir < 0 && cylinder > 0)) {
276: cylinder += dir;
277: }
278: }
279:
280: // ドライブセレクト入力 (DRIVE_SELECT, DISK_TYPE_SELECT 信号)
281: void
282: FDDDevice::SetDriveSelect(uint sel, bool is_2dd_)
283: {
284: if (is_2dd_) {
285: putlog(0, "Select 2DD (NOT IMPLEMENTED)");
286: }
287:
288: selected = (sel == unit);
289:
290: ChangeStatus();
291: }
292:
293: // MOTOR_ON 信号入力
294: void
295: FDDDevice::SetMotorOn(bool motor_on_)
296: {
297: motor_on = motor_on_;
298: ChangeStatus();
299: }
300:
301: void
1.1.1.6 root 302: FDDDevice::EventCallback(Event *ev)
1.1 root 303: {
304: // モータが回転している間は1バイトずつ進める
305: pos++;
306: if (pos >= MFM_TRACK_RAW_SIZE) {
307: pos = 0;
308: }
309:
310: switch (motor_state) {
311: case READY:
1.1.1.2 root 312: scheduler->StartEvent(ev);
1.1 root 313: break;
314:
315: case SPIN_UP:
316: // 増速中
1.1.1.6 root 317: if (event->time == 16_usec) {
1.1 root 318: motor_state = READY;
319: // READY になったので FDC に通知
1.1.1.2 root 320: fdc->ReadyChanged(unit, true);
1.1 root 321: } else {
1.1.1.6 root 322: if (event->time < 16_usec) {
1.1 root 323: // オーバーシュート対応
1.1.1.6 root 324: event->time = 16_usec;
1.1 root 325: } else {
1.1.1.6 root 326: event->time /= 2;
1.1 root 327: }
1.1.1.7 ! root 328: putlog(4, "SPIN_UP next=%uusec", (uint)tsec_to_usec(event->time));
1.1 root 329: }
1.1.1.2 root 330: scheduler->StartEvent(ev);
1.1 root 331: break;
332:
333: case SPIN_DOWN:
1.1.1.6 root 334: if (event->time >= 262144_usec) {
1.1 root 335: motor_state = STOPPED;
336: // イベント停止、ここでは time も 0 クリアしておく。
1.1.1.6 root 337: event->time = 0;
1.1 root 338: } else {
1.1.1.6 root 339: event->time *= 2;
1.1.1.2 root 340: scheduler->StartEvent(ev);
1.1.1.7 ! root 341: putlog(4, "SPIN_DOWN next=%uusec", (uint)tsec_to_usec(event->time));
1.1 root 342: }
343: break;
344:
345: default:
346: break;
347: }
348: }
349:
350: // ドライブの状態が変わった。
351: // メディア挿入、排出、ドライブセレクト変更など。
352: void
353: FDDDevice::ChangeStatus()
354: {
355: bool active = medium_loaded && motor_on;
356:
357: // モータ動作の決定
358: if (IsMotorActive() == false && active) {
359: // ディスクが挿入されていて、オフ -> オン
360: DoMotorOn();
361: } else if (IsMotorActive() && active == false) {
362: // オン -> オフ
363: DoMotorOff();
364: }
365:
366: // アクセス LED の決定
367: if (active && selected) {
368: access_led = LED::RED;
369: } else {
370: if (medium_loaded) {
371: access_led = LED::GREEN;
372: } else {
373: if (is_blink) {
374: access_led = LED::BLINK;
375: } else {
376: access_led = LED::OFF;
377: }
378: }
379: }
380: }
381:
382: void
383: FDDDevice::DoMotorOn()
384: {
385: putlog(1, "Motor On");
386:
387: if (motor_state == STOPPED) {
388: // 始動。
389: // 262144_usec/1バイトの回転速度から2倍ずつ速くしていって
390: // 16_usec/1バイトまで上げると定常状態とする。
391: // 仕様では 500msec で READY になるとあるが、ここでは 2 のべき乗を
392: // 使っているので、約 524msec かかる。細かいことは気にしない。
393: motor_state = SPIN_UP;
1.1.1.6 root 394: event->time = 262144_usec;
1.1.1.7 ! root 395: putlog(4, "SPIN_UP next=%uusec", (uint)tsec_to_usec(event->time));
1.1 root 396: } else if (motor_state == SPIN_DOWN) {
397: // 細かいことは無視して、増速に転じる
398: motor_state = SPIN_UP;
399: } else {
1.1.1.4 root 400: VMPANIC("DoMotorOn called on motor_state=%u", (uint)motor_state);
1.1 root 401: }
402:
1.1.1.2 root 403: scheduler->RestartEvent(event);
1.1 root 404: }
405:
406: void
407: FDDDevice::DoMotorOff()
408: {
409: putlog(1, "Motor Off");
410:
411: if (medium_loaded == false) {
412: // 物理的には多少モータは慣性回転するはずではあるが、ディスクは
413: // もうないので。
414: motor_state = STOPPED;
1.1.1.2 root 415: fdc->ReadyChanged(unit, false);
1.1 root 416: } else if (motor_state == READY) {
417: // 減速に転じる。
418: // 細かいことは気にせず増速と同様に倍々で減速していくので停止まで
419: // 約 524msec かかる。細かいことは気にしない。
420: motor_state = SPIN_DOWN;
421: // 定常回転 (READY) ではなくなった
1.1.1.2 root 422: fdc->ReadyChanged(unit, false);
1.1 root 423: } else if (motor_state == SPIN_UP) {
424: // 細かいことは無視して、減速に転じる。
425: motor_state = SPIN_DOWN;
426: } else {
1.1.1.4 root 427: VMPANIC("DoMotorOff called on motor_state=%u", (uint)motor_state);
1.1 root 428: }
429:
1.1.1.2 root 430: scheduler->RestartEvent(event);
1.1 root 431: }
432:
433: // ドライブコントロールのセット
434: // DRIVE # が '1' -> '0' に変化したときのみ呼び出される。
435: void
436: FDDDevice::SetDriveCtrl(uint8 data)
437: {
438: putlog(2, "Drive Control <- $%02x", data);
439:
440: // ログレベルが 1 なら変化時だけ、2 以上なら試行を全部表示?
441: auto islog = [&](bool expr) {
442: if (loglevel >= 2) {
443: return true;
444: } else if (loglevel == 1) {
445: if (expr) {
446: return true;
447: }
448: }
449: return false;
450: };
451:
452: // アクセスランプ点滅 (メディアが入っていない時のみ)
453: if (medium_loaded == false) {
454: // LED を点滅させておいてディスクを入れたら LED は点灯になるはず。
455: // そのままの状態でディスクを抜いたとき LED が点滅に戻るのか
456: // 消灯するのかは未調査。現状は点滅に戻るように実装してある。
457:
458: if ((data & 0x80)) {
459: if (islog(is_blink == false)) {
460: putlogn("LED Blink");
461: }
1.1.1.2 root 462: blink_start = scheduler->GetVirtTime();
1.1 root 463: is_blink = true;
464: } else {
465: if (islog(is_blink == true)) {
466: putlogn("LED Off");
467: }
468: is_blink = false;
469: }
470: } else {
471: if ((data & 0x40)) {
472: if (islog(eject_enable == true)) {
473: putlogn("Eject button disabled");
474: }
475: eject_enable = false;
476: } else {
477: if (islog(eject_enable == false)) {
478: putlogn("Eject button enabled");
479: }
480: eject_enable = true;
481: }
482:
483: if ((data & 0x20)) {
484: if (islog(medium_loaded == true)) {
485: putlogn("Eject");
486: }
487: UnloadDisk(true);
488: }
489: }
490: ChangeStatus();
491: }
492:
493: // 1 バイト取得
494: uint8
495: FDDDevice::ReadByte()
496: {
497: return trackbuf.buf[pos];
498: }
499:
500: // 1 バイト書き込み
501: void
502: FDDDevice::WriteByte(uint8 data)
503: {
504: trackbuf.buf[pos] = data;
505: }
506:
507: // 折り返してはいけない
508: uint8
509: FDDDevice::PeekByte(int offset) const
510: {
511: return trackbuf.buf[pos + offset];
512: }
513:
514: // 次のマークまたはインデックスホールまでのバイト距離を返す。
515: // 見つからないときは -1 を返す。
516: int
517: FDDDevice::GetDistanceToMark() const
518: {
519: if (pos == 0) {
520: return 0;
521: }
522: for (int i = 0; i < trackbuf.markpos.size(); i++) {
523: if (pos <= trackbuf.markpos[i]) {
524: return trackbuf.markpos[i] - pos;
525: }
526: }
527: return -1;
528: }
529:
530: // 現在のシリンダ位置の、hd で指定したトラックを読み込む
531: void
532: FDDDevice::ReadTrack(int hd)
533: {
534: uint32 lba = (cylinder * nhead + hd) * nsect;
535:
536: uint32 start = lba * sectsize;
537: uint32 len = nsect * sectsize;
538:
539: std::vector<uint8> buf(len);
540: image.Read(buf.data(), start, len);
541:
542: int bufpos = 0;
543:
544: // MFM のみサポート
545:
546: trackbuf.Clear();
547: trackbuf.lba = lba;
548:
549: // トラックデータ
550: // 本当は FDC が SYNC を検出しているのだが、
551: // AppendSync() によって SYNC の位置を別途記録することで対応している。
552:
553: trackbuf.Begin();
554:
555: // トラックヘッダ
556: trackbuf.Append(0x4e, 80); // Gap4a
557: trackbuf.Append(0x00, 12); // SYNC
558: trackbuf.Append(0xc2, 3); // IAM
559: trackbuf.AppendMark(MARK_IAM); // IAM マーク位置
560: trackbuf.Append(0x4e, 50); // Gap1
561:
562: for (int i = 0; i < nsect; i++) {
563: uint8 c = cylinder; // C は 0 始まり
564: uint8 h = hd; // H は 0 か 1
565: uint8 r = i + 1; // R は 1 始まり
566: uint8 n = format_n;
567: uint8 crc[2] {}; // XXX CRC は未実装
568: uint8 datacrc[2] {};
569:
570: trackbuf.Append(0x00, 12); // SYNC
571: trackbuf.Append(0xa1, 3); // IDAM
572: trackbuf.AppendMark(MARK_IDAM);
573: trackbuf.Append(c);
574: trackbuf.Append(h);
575: trackbuf.Append(r);
576: trackbuf.Append(n);
577: trackbuf.Append(crc[0]);
578: trackbuf.Append(crc[1]);
579: trackbuf.Append(0x4e, 22); // Gap2
580:
581: trackbuf.Append(0x00, 12); // SYNC
582: trackbuf.Append(0xa1, 3); // DAM/DDAM
583: if (true/*dam*/) {
584: trackbuf.AppendMark(MARK_DAM);
585: } else {
586: trackbuf.AppendMark(MARK_DDAM);
587: }
588:
589: for (int j = 0; j < sectsize; j++) {
590: trackbuf.Append(buf[bufpos++]);
591: }
592: trackbuf.Append(datacrc[0]);
593: trackbuf.Append(datacrc[1]);
594:
595: trackbuf.Append(0, gap3); // Gap3
596: }
597: trackbuf.Append(0, gap4b); // Gap4b
598:
599: trackbuf.End(); // トラック終わり
600: }
601:
602: // トラックバッファをイメージにフラッシュ
603: void
604: FDDDevice::WriteTrack()
605: {
606: uint32 lba = trackbuf.lba;
607:
608: uint32 start = lba * sectsize;
609: uint32 len = nsect * sectsize;
610:
611: std::vector<uint8> buf(len);
612:
613: // MFM のみサポート
614:
615: int bufpos = 0;
616:
617: // トラックデータからデータを再構成
618:
619: for (int i = 0; i < trackbuf.markpos.size(); i++) {
620: int p = trackbuf.markpos[i];
621: if (p < trackbuf.buf.size()) {
622: uint8 d = trackbuf.buf[p];
623: if (d == MARK_DAM || d == MARK_DDAM) {
624: p++;
625: for (int j = 0; j < sectsize; j++) {
626: buf[bufpos++] = trackbuf.buf[p++];
627: }
628: }
629: }
630: }
631: if (bufpos != len) {
632: VMPANIC("invalid track");
633: }
634:
635: image.Write(buf.data(), start, len);
636: }
637:
638: // バックエンドのディスクイメージを開く。
639: // 成功すれば true、失敗すれば false を返す。(ロードされれば true を返す
640: // のではないが、medium_loaded が同じ値になるので使いまわしている)
641: bool
642: FDDDevice::LoadDisk(const std::string& pathname_)
643: {
644: bool ejected;
645: off_t size;
1.1.1.4 root 646: int w_ok;
1.1 root 647: bool read_only;
648:
649: // すでにあればクローズ (ここでは通知は行わない)
650: ejected = UnloadDisk_internal();
651:
652: // 新しいイメージをオープン(する準備)
653: pathname = pathname_;
654: if (image.CreateHandler(pathname) == false) {
655: goto done;
656: }
657:
1.1.1.4 root 658: w_ok = image.IsWriteable();
659: if (w_ok < 0) {
660: goto done;
661: }
662:
663: // 書き込みモードをここで確定
664: if (w_ok) {
665: if (write_ignore) {
666: write_mode = RW::WriteIgnore;
667: } else {
668: write_mode = RW::Writeable;
669: }
670: } else {
671: write_mode = RW::WriteProtect;
672: }
673:
674: // ここでオープン
675: read_only = (GetWriteMode() != RW::Writeable);
676: if (image.Open(read_only, write_ignore) == false) {
1.1 root 677: goto done;
678: }
679:
1.1.1.4 root 680: size = image.GetSize();
1.1 root 681: if (size == 1261568) {
682: // Human68k 2HD フォーマットとみなす
683: // 77track, 2side, 8sector, 1024bytes/sector
684: format_name = "2HD";
685: ncyl = 77;
686: nhead = 2;
687: nsect = 8;
688: sectsize = 1024;
689: format_n = 3;
690: gap3 = 0x74;
691: } else if (size == 1228800) {
692: // NetBSD 2HC フォーマットとみなす
693: // 80track, 2side, 15sector, 512bytes/sector
694: format_name = "2HC";
695: ncyl = 80;
696: nhead = 2;
697: nsect = 15;
698: sectsize = 512;
699: format_n = 2;
700: gap3 = 0x54;
701: } else {
702: warnx("%s: Unsuppoted image, size(%ju)",
703: pathname.c_str(), (uintmax_t)size);
704: goto done;
705: }
706:
707: gap4b = (MFM_TRACK_RAW_SIZE - MFM_TRACK_HEADER)
708: - (sectsize + MFM_SECTOR_HEADER + gap3) * nsect;
709:
710: // 書き込み無視なら一応ログ出力
711: if (GetWriteMode() == RW::WriteIgnore) {
712: putmsg(0, "write is ignored");
713: }
714:
715: medium_loaded = true;
716: putmsg(0, "%s format", format_name.c_str());
717: putlog(1, "Medium loaded");
718:
719: // オートリキャリブレート
720: cylinder = 0;
721:
722: done:
723: if (medium_loaded == false) {
724: // ここで状態をクリアする
725: Clear();
726: }
727:
728: // 変化があれば
729: if (ejected || medium_loaded) {
730: ChangeStatus();
1.1.1.2 root 731: pedec->AssertINT(this);
1.1 root 732: // 通知
733: MediaChanged();
734: }
735:
736: return medium_loaded;
737: }
738:
739: // バックエンドのディスクイメージを閉じる。
740: // オープンされてなければ何もしない。
741: void
742: FDDDevice::UnloadDisk(bool force)
743: {
744: if (eject_enable || force) {
745: if (UnloadDisk_internal()) {
746: // 実際に閉じたら通知
747: MediaChanged();
748: }
749: }
750: }
751:
752: // バックエンドのディスクイメージを閉じる (内部用)。
753: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
754: // ここでは通知は行わない。
755: bool
756: FDDDevice::UnloadDisk_internal()
757: {
758: if ((bool)image == false) {
759: return false;
760: }
761:
1.1.1.2 root 762: pedec->AssertINT(this);
1.1 root 763:
764: Clear();
765:
766: // 取り出し禁止はされてても解除する
767: if (eject_enable == false) {
768: eject_enable = true;
769: }
770:
771: putlog(1, "Medium unloaded");
772: return true;
773: }
774:
775: // ディスク状態をクリアする
776: // (メディアを取り外した時など)
777: void
778: FDDDevice::Clear()
779: {
780: medium_loaded = false;
781: ChangeStatus();
782: image.Close();
783: pathname.clear();
784: write_mode = RW::Writeable;
785: }
786:
787: // メディア状態変更を UI に通知する
788: void
789: FDDDevice::MediaChanged() const
790: {
1.1.1.6 root 791: gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_CHANGE, unit);
1.1 root 792: }
793:
794: // メディアを挿入する。
795: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
796: void
797: FDDDevice::LoadDiskUI(const std::string& pathname_)
798: {
799: new_pathname = pathname_;
1.1.1.2 root 800: scheduler->SendMessage(MessageID::FDD_LOAD(unit));
1.1 root 801: }
802:
803: // メディアを排出する。
804: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
805: void
806: FDDDevice::UnloadDiskUI(bool force)
807: {
1.1.1.2 root 808: scheduler->SendMessage(MessageID::FDD_UNLOAD(unit), force);
1.1 root 809: }
810:
811: // メディア挿入メッセージコールバック
812: void
1.1.1.3 root 813: FDDDevice::LoadMessage(MessageID msgid, uint32 arg)
1.1 root 814: {
815: if (LoadDisk(new_pathname) == false) {
816: // 失敗したら UI に通知
1.1.1.6 root 817: gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_FAILED);
1.1 root 818: }
819: }
820:
821: // メディア排出メッセージコールバック
822: void
1.1.1.3 root 823: FDDDevice::UnloadMessage(MessageID msgid, uint32 arg)
1.1 root 824: {
825: bool force = arg;
826: UnloadDisk(force);
827: }
828:
829: // アクセス LED の状態を取得。
830: // ここで返すのは OFF, GREEN, RED の3通り。
831: FDDDevice::LED
832: FDDDevice::GetAccessLED() const
833: {
834: if (access_led == LED::BLINK) {
835: // 点滅状態はここで作成。
836: // 点滅開始から 0.5秒は点灯、次の 0.5秒を消灯とする。
837: // XXX 間隔は適当
1.1.1.2 root 838: uint64 time = scheduler->GetVirtTime() - blink_start;
1.1 root 839: time %= 1_sec;
840: if (time < 500_msec) {
841: return LED::GREEN;
842: } else {
843: return LED::OFF;
844: }
845: } else {
846: // それ以外はそのまま返せる
847: return access_led;
848: }
849: }
850:
851: // イジェクト LED の状態を取得。点灯なら true
852: bool
853: FDDDevice::GetEjectLED() const
854: {
855: // メディアが挿入されていて、イジェクト許可なら点灯。
856: return (medium_loaded && eject_enable);
857: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.