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