|
|
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.