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