|
|
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"
1.1.1.6 ! root 21: #include "event.h"
1.1 root 22: #include "fdc.h"
23: #include "mainapp.h"
24: #include "pedec.h"
25: #include "scheduler.h"
26: #include "uimessage.h"
27:
28: #define MFM_TRACK_HEADER (80 + 12 + 3 + 1 + 50)
29: #define MFM_SECTOR_HEADER (12 + 3 + 1 + 4 + 2 + 22 + 12 + 3 + 1 + 2)
30: #define MFM_TRACK_RAW_SIZE (10416)
31:
1.1.1.2 root 32: //
33: // フロッピーディスクの 1 トラック
34: //
35:
36: // トラックデータの記録開始
37: void
38: FDTrack::Begin()
39: {
40: markpos.push_back(buf.size());
41: }
42:
43: // トラックデータの記録終了
44: void
45: FDTrack::End()
46: {
47: assert(buf.size() == MFM_TRACK_RAW_SIZE);
48:
49: markpos.push_back(buf.size());
50: }
51:
52: // トラックデータに data を追加
1.1 root 53: void
54: FDTrack::Append(uint8 data)
55: {
56: buf.push_back(data);
57: }
58:
1.1.1.2 root 59: // トラックデータに data を count 個追加
1.1 root 60: void
61: FDTrack::Append(uint8 data, int count)
62: {
63: for (int i = 0; i < count; i++) {
64: Append(data);
65: }
66: }
67:
68: // トラックデータにマークを追加
69: void
70: FDTrack::AppendMark(uint8 data)
71: {
72: // マーク位置を記録
73: markpos.push_back(buf.size());
74: Append(data);
75: }
76:
77: void
78: FDTrack::Clear()
79: {
80: buf.clear();
81: markpos.clear();
82: }
83:
84:
1.1.1.2 root 85: //
86: // フロッピードライブ
87: //
1.1 root 88:
1.1.1.2 root 89: // コンストラクタ
1.1.1.4 root 90: FDDDevice::FDDDevice(uint unit_)
1.1.1.2 root 91: : inherited(OBJ_FDD(unit_))
1.1 root 92: {
93: unit = unit_;
94: }
95:
1.1.1.2 root 96: // デストラクタ
1.1 root 97: FDDDevice::~FDDDevice()
98: {
99: }
100:
101: // 初期化
102: bool
103: FDDDevice::Init()
104: {
1.1.1.2 root 105: fdc = GetFDCDevice();
106: pedec = GetPEDECDevice();
107:
1.1.1.4 root 108: const std::string keybody = string_format("fd%u-", unit);
1.1 root 109: const std::string imgkey = keybody + "image";
110: const std::string wikey = keybody + "writeignore";
111:
112: const ConfigItem& itemimg = gConfig->Find(imgkey);
113: const std::string& filename = itemimg.AsString();
114:
115: // UI からの通知を受け取るイベントを用意
1.1.1.2 root 116: scheduler->ConnectMessage(MessageID::FDD_LOAD(unit), this,
1.1.1.3 root 117: ToMessageCallback(&FDDDevice::LoadMessage));
1.1.1.2 root 118: scheduler->ConnectMessage(MessageID::FDD_UNLOAD(unit), this,
1.1.1.3 root 119: ToMessageCallback(&FDDDevice::UnloadMessage));
1.1 root 120:
1.1.1.6 ! root 121: auto evman = GetEventManager();
! 122: event = evman->Regist(this,
! 123: ToEventCallback(&FDDDevice::EventCallback),
! 124: GetName());
1.1.1.4 root 125:
1.1 root 126: // オープン前に writeignore をチェック。
127: // この後パス名を処理するとすぐに UI に通知を出すため、それより前で
128: // 行わないといけない。うーん。
129: const ConfigItem& wi = gConfig->Find(wikey);
130: write_ignore = wi.AsInt();
131:
132: // 設定時点でファイルが指定されていればオープン
133: if (filename.empty() == false) {
1.1.1.3 root 134: std::string path = gMainApp.NormalizePath(filename);
1.1 root 135: if (LoadDisk(path) == false) {
136: return false;
137: }
138: }
139:
140: return true;
141: }
142:
143: // リセット
144: void
145: FDDDevice::ResetHard(bool poweron)
146: {
147: if (poweron) {
148: motor_state = STOPPED;
149: }
150:
151: // XXX どこでやる?
152: eject_enable = true;
153: }
154:
155: // モニタの下請け (fdc から呼ばれる)。
156: // y = 11 以降が各ドライブの状態で、このうち x = 26 以降が FDD の担当。
157: // y = 16 以降は FDD のアクセスマップ。
158: void
1.1.1.4 root 159: FDDDevice::MonitorUpdateFDD(TextScreen& screen, uint hd)
1.1 root 160: {
161: /* 1 2 3 4 5 6 7
162: 0123456789012345678901234567890123456789012345678901234567890123456789012345678
163: ready accled eject fmt C H R N
164: stop blink disable 2HC 99 1 99 2(1024)
165: Unit0: HD=1 cyl=nn/nn pos=12345/12345
166: XX>01234567890123456 01234567890123456| XX>01234567890123456 01234567890123456|
167: */
168:
169: int uy = 11 + unit;
170: int ux = 26;
171:
172: // モータ状態
173: const char * const motor_state_str[] = {
174: "Stop",
175: "Down",
176: "Up",
177: "Ready",
178: };
179: screen.Print(ux, uy, "%s", motor_state_str[(int)motor_state]);
180:
181: // アクセス LED 状態
182: const char * const access_led_str[] = {
183: "Off",
184: "Green",
185: "Red",
186: "Blink",
187: };
188: screen.Print(ux + 6, uy, "%s", access_led_str[(int)access_led]);
189:
190: // イジェクト可否 (LED 状態)
191: const char *eject_str;
192: if (medium_loaded) {
193: if (eject_enable) {
194: eject_str = "Enable";
195: } else {
196: eject_str = "Disable";
197: }
198: } else {
199: eject_str = "Off";
200: }
201: screen.Puts(ux + 13, uy, eject_str);
202:
203: if (medium_loaded) {
204: // フォーマットと CHRN
1.1.1.4 root 205: screen.Print(ux + 20, uy, "%4s %2u %u %2u %u(%u)",
1.1 root 206: format_name.c_str(), ncyl, nhead, nsect, format_n, sectsize);
207: }
208:
209: uy = 16 + (unit / 2) * 10;
210: ux = (unit % 2) * 41;
211:
1.1.1.4 root 212: screen.Print(ux, uy++, "Unit %u", unit);
1.1 root 213: // 横ガイド
214: for (int i = 0; i < 8; i++) {
215: screen.Puts(ux + 2, uy + i, "|");
216: screen.Puts(ux + 38, uy + i, "|");
217: }
218:
219: // メディアが入ってなければここで終わり
220: if (IsMediumLoaded() == false) {
221: return;
222: }
223:
224: TA selattr = selected ? TA::Em : TA::Normal;
1.1.1.4 root 225: screen.Print(ux, uy - 1, selattr, "Unit %u: HD=%u cyl=%2u/%u pos=%5u/%u",
1.1 root 226: unit, hd, cylinder, ncyl, pos, MFM_TRACK_RAW_SIZE);
227:
228: // ヘッドの現在位置。
229: // 横は全長を当分割、縦は1行で10トラック固定にする
230: int headx = (double)pos / MFM_TRACK_RAW_SIZE * 17;
231: int heady = (double)cylinder / 10;
232:
233: // 片面ディスクはサポートしていないが一応
234: for (int h = 0; h < nhead; h++) {
235: for (int i = 0; i < 8; i++) {
236: TA trkattr = (__predict_false(selected && i == heady && h == hd))
237: ? TA::Em : TA::Normal;
238: screen.Puts(ux + h * 18 + 3, uy + i, trkattr,
239: // 01234567890123456
240: ".................");
241: }
242: }
243:
244: // ヘッド位置
1.1.1.4 root 245: screen.Print(ux, uy + heady, selattr, "%2u>", cylinder);
1.1 root 246: screen.Puts(ux + 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
1.1.1.6 ! root 299: FDDDevice::EventCallback(Event *ev)
1.1 root 300: {
301: // モータが回転している間は1バイトずつ進める
302: pos++;
303: if (pos >= MFM_TRACK_RAW_SIZE) {
304: pos = 0;
305: }
306:
307: switch (motor_state) {
308: case READY:
1.1.1.2 root 309: scheduler->StartEvent(ev);
1.1 root 310: break;
311:
312: case SPIN_UP:
313: // 増速中
1.1.1.6 ! root 314: if (event->time == 16_usec) {
1.1 root 315: motor_state = READY;
316: // READY になったので FDC に通知
1.1.1.2 root 317: fdc->ReadyChanged(unit, true);
1.1 root 318: } else {
1.1.1.6 ! root 319: if (event->time < 16_usec) {
1.1 root 320: // オーバーシュート対応
1.1.1.6 ! root 321: event->time = 16_usec;
1.1 root 322: } else {
1.1.1.6 ! root 323: event->time /= 2;
1.1 root 324: }
1.1.1.6 ! root 325: putlog(4, "SPIN_UP next=%uusec", (uint)(event->time / 1_usec));
1.1 root 326: }
1.1.1.2 root 327: scheduler->StartEvent(ev);
1.1 root 328: break;
329:
330: case SPIN_DOWN:
1.1.1.6 ! root 331: if (event->time >= 262144_usec) {
1.1 root 332: motor_state = STOPPED;
333: // イベント停止、ここでは time も 0 クリアしておく。
1.1.1.6 ! root 334: event->time = 0;
1.1 root 335: } else {
1.1.1.6 ! root 336: event->time *= 2;
1.1.1.2 root 337: scheduler->StartEvent(ev);
1.1.1.6 ! root 338: putlog(4, "SPIN_DOWN next=%uusec", (uint)(event->time / 1_usec));
1.1 root 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;
1.1.1.6 ! root 391: event->time = 262144_usec;
! 392: putlog(4, "SPIN_UP next=%uusec", (uint)(event->time / 1_usec));
1.1 root 393: } else if (motor_state == SPIN_DOWN) {
394: // 細かいことは無視して、増速に転じる
395: motor_state = SPIN_UP;
396: } else {
1.1.1.4 root 397: VMPANIC("DoMotorOn called on motor_state=%u", (uint)motor_state);
1.1 root 398: }
399:
1.1.1.2 root 400: scheduler->RestartEvent(event);
1.1 root 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;
1.1.1.2 root 412: fdc->ReadyChanged(unit, false);
1.1 root 413: } else if (motor_state == READY) {
414: // 減速に転じる。
415: // 細かいことは気にせず増速と同様に倍々で減速していくので停止まで
416: // 約 524msec かかる。細かいことは気にしない。
417: motor_state = SPIN_DOWN;
418: // 定常回転 (READY) ではなくなった
1.1.1.2 root 419: fdc->ReadyChanged(unit, false);
1.1 root 420: } else if (motor_state == SPIN_UP) {
421: // 細かいことは無視して、減速に転じる。
422: motor_state = SPIN_DOWN;
423: } else {
1.1.1.4 root 424: VMPANIC("DoMotorOff called on motor_state=%u", (uint)motor_state);
1.1 root 425: }
426:
1.1.1.2 root 427: scheduler->RestartEvent(event);
1.1 root 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: }
1.1.1.2 root 459: blink_start = scheduler->GetVirtTime();
1.1 root 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;
1.1.1.4 root 643: int w_ok;
1.1 root 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:
1.1.1.4 root 655: w_ok = image.IsWriteable();
656: if (w_ok < 0) {
657: goto done;
658: }
659:
660: // 書き込みモードをここで確定
661: if (w_ok) {
662: if (write_ignore) {
663: write_mode = RW::WriteIgnore;
664: } else {
665: write_mode = RW::Writeable;
666: }
667: } else {
668: write_mode = RW::WriteProtect;
669: }
670:
671: // ここでオープン
672: read_only = (GetWriteMode() != RW::Writeable);
673: if (image.Open(read_only, write_ignore) == false) {
1.1 root 674: goto done;
675: }
676:
1.1.1.4 root 677: size = image.GetSize();
1.1 root 678: if (size == 1261568) {
679: // Human68k 2HD フォーマットとみなす
680: // 77track, 2side, 8sector, 1024bytes/sector
681: format_name = "2HD";
682: ncyl = 77;
683: nhead = 2;
684: nsect = 8;
685: sectsize = 1024;
686: format_n = 3;
687: gap3 = 0x74;
688: } else if (size == 1228800) {
689: // NetBSD 2HC フォーマットとみなす
690: // 80track, 2side, 15sector, 512bytes/sector
691: format_name = "2HC";
692: ncyl = 80;
693: nhead = 2;
694: nsect = 15;
695: sectsize = 512;
696: format_n = 2;
697: gap3 = 0x54;
698: } else {
699: warnx("%s: Unsuppoted image, size(%ju)",
700: pathname.c_str(), (uintmax_t)size);
701: goto done;
702: }
703:
704: gap4b = (MFM_TRACK_RAW_SIZE - MFM_TRACK_HEADER)
705: - (sectsize + MFM_SECTOR_HEADER + gap3) * nsect;
706:
707: // 書き込み無視なら一応ログ出力
708: if (GetWriteMode() == RW::WriteIgnore) {
709: putmsg(0, "write is ignored");
710: }
711:
712: medium_loaded = true;
713: putmsg(0, "%s format", format_name.c_str());
714: putlog(1, "Medium loaded");
715:
716: // オートリキャリブレート
717: cylinder = 0;
718:
719: done:
720: if (medium_loaded == false) {
721: // ここで状態をクリアする
722: Clear();
723: }
724:
725: // 変化があれば
726: if (ejected || medium_loaded) {
727: ChangeStatus();
1.1.1.2 root 728: pedec->AssertINT(this);
1.1 root 729: // 通知
730: MediaChanged();
731: }
732:
733: return medium_loaded;
734: }
735:
736: // バックエンドのディスクイメージを閉じる。
737: // オープンされてなければ何もしない。
738: void
739: FDDDevice::UnloadDisk(bool force)
740: {
741: if (eject_enable || force) {
742: if (UnloadDisk_internal()) {
743: // 実際に閉じたら通知
744: MediaChanged();
745: }
746: }
747: }
748:
749: // バックエンドのディスクイメージを閉じる (内部用)。
750: // 実際に閉じれば true を返す。オープンされてなければ何もせず false を返す。
751: // ここでは通知は行わない。
752: bool
753: FDDDevice::UnloadDisk_internal()
754: {
755: if ((bool)image == false) {
756: return false;
757: }
758:
1.1.1.2 root 759: pedec->AssertINT(this);
1.1 root 760:
761: Clear();
762:
763: // 取り出し禁止はされてても解除する
764: if (eject_enable == false) {
765: eject_enable = true;
766: }
767:
768: putlog(1, "Medium unloaded");
769: return true;
770: }
771:
772: // ディスク状態をクリアする
773: // (メディアを取り外した時など)
774: void
775: FDDDevice::Clear()
776: {
777: medium_loaded = false;
778: ChangeStatus();
779: image.Close();
780: pathname.clear();
781: write_mode = RW::Writeable;
782: }
783:
784: // メディア状態変更を UI に通知する
785: void
786: FDDDevice::MediaChanged() const
787: {
1.1.1.6 ! root 788: gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_CHANGE, unit);
1.1 root 789: }
790:
791: // メディアを挿入する。
792: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
793: void
794: FDDDevice::LoadDiskUI(const std::string& pathname_)
795: {
796: new_pathname = pathname_;
1.1.1.2 root 797: scheduler->SendMessage(MessageID::FDD_LOAD(unit));
1.1 root 798: }
799:
800: // メディアを排出する。
801: // UI スレッドで実行されるので、VM スレッドに通知するだけ。
802: void
803: FDDDevice::UnloadDiskUI(bool force)
804: {
1.1.1.2 root 805: scheduler->SendMessage(MessageID::FDD_UNLOAD(unit), force);
1.1 root 806: }
807:
808: // メディア挿入メッセージコールバック
809: void
1.1.1.3 root 810: FDDDevice::LoadMessage(MessageID msgid, uint32 arg)
1.1 root 811: {
812: if (LoadDisk(new_pathname) == false) {
813: // 失敗したら UI に通知
1.1.1.6 ! root 814: gMainApp.GetUIMessage()->Post(UIMessage::FDD_MEDIA_FAILED);
1.1 root 815: }
816: }
817:
818: // メディア排出メッセージコールバック
819: void
1.1.1.3 root 820: FDDDevice::UnloadMessage(MessageID msgid, uint32 arg)
1.1 root 821: {
822: bool force = arg;
823: UnloadDisk(force);
824: }
825:
826: // アクセス LED の状態を取得。
827: // ここで返すのは OFF, GREEN, RED の3通り。
828: FDDDevice::LED
829: FDDDevice::GetAccessLED() const
830: {
831: if (access_led == LED::BLINK) {
832: // 点滅状態はここで作成。
833: // 点滅開始から 0.5秒は点灯、次の 0.5秒を消灯とする。
834: // XXX 間隔は適当
1.1.1.2 root 835: uint64 time = scheduler->GetVirtTime() - blink_start;
1.1 root 836: time %= 1_sec;
837: if (time < 500_msec) {
838: return LED::GREEN;
839: } else {
840: return LED::OFF;
841: }
842: } else {
843: // それ以外はそのまま返せる
844: return access_led;
845: }
846: }
847:
848: // イジェクト LED の状態を取得。点灯なら true
849: bool
850: FDDDevice::GetEjectLED() const
851: {
852: // メディアが挿入されていて、イジェクト許可なら点灯。
853: return (medium_loaded && eject_enable);
854: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.