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