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