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