|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.9 root 7: //
1.1.1.10! root 8: // FDC (uPD72065B) + I/O コントローラ
1.1.1.9 root 9: //
10:
1.1.1.10! root 11: // MOTOR ON 信号
! 12: //
! 13: // X68030 の回路図によれば、簡略すると以下のように接続されている。
! 14: //
! 15: // PEDEC SED9420A FDD
! 16: // -------+ +---------------+ +-----------
! 17: // ~TRIG |-----| TRIG TMOUT |------| ~MOTOR ON
! 18: //
! 19: // SED9420A の TRIG は内蔵単安定マルチバイブレータのトリガ端子となっている。
! 20: // TRIG がアサートされている間 TMOUT をアサートし、TRIG がネゲートされると
! 21: // 一定時間後に TMOUT をネゲートする。
! 22: // その時間は SED9420A の CR 端子に接続された CR 回路によって決まってくるが
! 23: // データシートには時間の計算方法が記されていない。X68030 の回路図では
! 24: //
! 25: // SED9420A
! 26: // ---------+
! 27: // CR |-----+---820kOhm---VCC1
! 28: // |
! 29: // 47uF
! 30: // |
! 31: // GND
! 32: //
! 33: // となっているので時定数は 38.54 秒である。
! 34: // この時定数と TMOUT の維持時間の正確な関係は分からないが、他の単安定マルチ
! 35: // では時定数時間とパルス幅が同じになるように調整されたもの(74HC123)もある
! 36: // ので、この時定数時間を TMOUT (MOTOR ON) 信号の維持時間と考えることにする。
! 37: //
! 38: // 参考文献
! 39: // 最新フロッピ・ディスク装置とその応用ノウハウ 高橋昇司 p.175
! 40: // https://archive.org/details/recent_floppy_disk_system_and_know_how/
! 41: // page/n175
! 42: //
! 43: //
! 44: // DRIVE SEL.x 信号とアクセスドライブセレクトレジスタ
! 45: //
! 46: // アクセスドライブセレクトレジスタ($e94007) の MOTOR_ON ビットが 1 の
! 47: // ときは、アクセスドライブ番号フィールドにより指定された DRIVE SEL.x 信号が
! 48: // アサートされる。MOTOR_ON ビットが 0 のときは、すべての DRIVE SEL.x 信号は
! 49: // ネゲートされて、どのドライブも選択されていない状態になる。
! 50: // 「ドライブセレクトイネーブル」とあるのは、こういう意味である。
! 51: //
! 52: //
! 53: // DRIVE SEL.x 信号と各信号の関係
! 54: //
! 55: // MOTOR ON 信号と DRIVE SEL.x 信号の考察を踏まえると、MOTOR ON 信号は
! 56: // ドライブが選択されているかどうかに関わらず、全ドライブが受け取らなければ
! 57: // ならない。そうでないとタイマーを入れている意味がない。
! 58: // FDD は自分が選択されているかどうかに関係なく、MOTOR ON 信号がアサート
! 59: // されればモータを回そうとし、ネゲートされればモータを止める。
! 60: // また、アクセスドライブセレクトレジスタの 2HD/2DD ビット、およびその
! 61: // 関係する信号である DISK TYPE SEL, MIN/STD, MFM/FM 周りも同様と考えられる。
! 62: // これらの信号は SED9420A に接続されるため、ドライブごとに扱うように
! 63: // なっていないからである。
! 64: // また、FDDINT についても、どこかのドライブで状態が変わったことを CPU に
! 65: // 通知する、という機能が要求されるべきであるから、OP.SEL にもよらず
! 66: // 割り込みがかかる、と推定される。
! 67: //
! 68: // DRIVE SEL.x でドライブごとに有効になる信号
! 69: // READY, INDEX, READ DATA, WRITE PROTECT, TRK00, WRITE GATE, WRITE DATA,
! 70: // STEP, DIRECTION
! 71: // ドライブに共通の信号
! 72: // MOTOR ON, DISK TYPE SEL, FDDINT
! 73: // どちらでも良い信号
! 74: // SIDE SELECT (たぶんドライブごと有効と思われる)
! 75: // OP.SEL.x でドライブごとに有効になる信号
! 76: // EJECT, EJECT MASK, LED BLINK, DISK IN, ERR DISK
! 77:
1.1 root 78: #include "fdc.h"
1.1.1.10! root 79: #include "bitops.h"
! 80: #include "config.h"
! 81: #include "dmac.h"
! 82: #include "fdd.h"
1.1.1.9 root 83: #include "mpu.h"
1.1.1.6 root 84: #include "pedec.h"
1.1 root 85: #include "scheduler.h"
86:
1.1.1.9 root 87: // グローバル参照用
88: FDCDevice *gFDC;
1.1.1.6 root 89:
1.1.1.10! root 90: // time [nsec] 後に呼び出す phase_event のコールバックをセットする。
! 91: // func の書式が面倒なのを省略して書きたいため。
! 92: #define CallAfter(func_, time_) do { \
! 93: phase_event.func = ToEventCallback(&FDCDevice::func_); \
! 94: phase_event.time = time_; \
! 95: gScheduler->RestartEvent(phase_event); \
! 96: } while (0)
! 97:
! 98:
1.1 root 99: // コンストラクタ
100: FDCDevice::FDCDevice()
1.1.1.8 root 101: : inherited("FDC")
1.1 root 102: {
103: devaddr = baseaddr;
104: devlen = 0x2000;
105:
1.1.1.10! root 106: phase_event.Regist("FDC Phase");
1.1.1.9 root 107:
1.1.1.10! root 108: poll_event.func = ToEventCallback(&FDCDevice::PollEventCallback);
! 109: poll_event.time = 1024_usec;
! 110: poll_event.Regist("FDC Polling");
! 111:
! 112: motor_event.func = ToEventCallback(&FDCDevice::MotorEventCallback);
! 113: motor_event.time = 38.54_sec;
! 114: motor_event.Regist("FDC SED9420A TMOUT");
! 115:
! 116: monitor.func = ToMonitorCallback(&FDCDevice::MonitorUpdate);
! 117: // サイズは Create() 時に確定する
! 118: monitor.Regist(ID_MONITOR_FDC);
1.1 root 119: }
120:
121: // デストラクタ
122: FDCDevice::~FDCDevice()
123: {
1.1.1.9 root 124: gFDC = NULL;
1.1 root 125: }
126:
1.1.1.10! root 127: bool
! 128: FDCDevice::Create()
! 129: {
! 130: // ドライブ数。
! 131: // fdd_list はランダムアクセスできる配列なので、歯抜け (#0 がなくて
! 132: // #1 があるみたいなの) もサポートするが、今の所設定方法がない。
! 133: const ConfigItem& item = gConfig->Find("fd-drive");
! 134: ndrive = item.AsInt();
! 135:
! 136: // X68k では上限4台。(LUNA はひとまず放置)
! 137: if (ndrive > 4) {
! 138: item.Err();
! 139: return false;
! 140: }
! 141:
! 142: // モニタサイズはここで決定。FDD は横に2台で、それ以上は縦に伸ばす。
! 143: monitor.SetSize(80, 15 + (ndrive / 2) * 10);
! 144:
! 145: // vector<unique_ptr> は面倒なので一旦 unique_ptr[] に確保して
! 146: // 生ポインタの vector を作る。うーん。
! 147: for (int i = 0; i < ndrive; i++) {
! 148: fdd_list[i].reset(new FDDDevice(i));
! 149: }
! 150: for (int i = 0; i < ndrive; i++) {
! 151: if ((bool)fdd_list[i]) {
! 152: fdd_vector.push_back(fdd_list[i].get());
! 153: } else {
! 154: fdd_vector.push_back(NULL);
! 155: }
! 156: }
! 157:
! 158: return true;
! 159: }
! 160:
1.1.1.9 root 161: // リセット
1.1 root 162: void
1.1.1.9 root 163: FDCDevice::ResetHard(bool poweron)
1.1 root 164: {
1.1.1.10! root 165: // uPD765 マニュアルによると、リセット信号によるリセットでは
! 166: // SRT, HUT, HLT は影響を受けない。
! 167:
! 168: // シリンダ位置 pcn はとりあえずわからないので放置。
! 169:
! 170: if (poweron) {
! 171: // READY ポーリングはリセットの影響を受けないはず
! 172: initialized = false;
! 173: gScheduler->StopEvent(motor_event);
! 174: }
! 175:
! 176: gScheduler->StopEvent(phase_event);
! 177: gScheduler->StopEvent(poll_event);
! 178: CommandPhase();
1.1 root 179: }
180:
181: uint64
1.1.1.7 root 182: FDCDevice::Read(uint32 offset)
1.1 root 183: {
1.1.1.5 root 184: gMPU->AddCycle(19); // InsideOut p.135
185:
1.1.1.7 root 186: switch (offset) {
1.1.1.4 root 187: case 0:
1.1.1.10! root 188: {
! 189: if (dack) {
! 190: // DACK が立っているときは A0 によらずデータレジスタをアクセス
! 191: return ReadData();
! 192: }
! 193:
! 194: uint8 stat = GetMSR();
! 195: putlog(4, "STAT -> $%02x", stat);
! 196: return stat;
! 197: }
1.1.1.4 root 198: case 1:
1.1 root 199: return ReadData();
1.1.1.4 root 200: case 2:
1.1 root 201: return ReadDriveStatus();
1.1.1.4 root 202: case 3:
203: return 0xff;
1.1 root 204: }
1.1.1.4 root 205: __unreachable();
1.1 root 206: }
207:
208: uint64
1.1.1.7 root 209: FDCDevice::Write(uint32 offset, uint32 data)
1.1 root 210: {
1.1.1.5 root 211: gMPU->AddCycle(19); // InsideOut p.135
212:
1.1.1.7 root 213: switch (offset) {
1.1.1.4 root 214: case 0:
1.1.1.10! root 215: if (dack) {
! 216: // DACK が立っているときは A0 によらずデータレジスタをアクセス
! 217: WriteDataReg(data);
! 218: return 0;
! 219: }
! 220:
! 221: // ここはフェーズに関わらず 0x34, 0x35, 0x36 のコマンドだけを
! 222: // いつでも受け付ける。RQM などの信号制御とは関係なく受け付ける、
! 223: // となっているはずなので直接ディスパッチする。
! 224: switch (data) {
! 225: case 0x34:
! 226: CmdResetStandby();
! 227: break;
! 228: case 0x35:
! 229: CmdSetStandby();
! 230: break;
! 231: case 0x36:
! 232: CmdSoftwareReset();
! 233: break;
! 234: default:
! 235: putlog(0, "Write DATA: command $%02x (NOT IMPLEMENTED)",
! 236: data);
! 237: break;
! 238: }
1.1.1.4 root 239: return 0;
1.1 root 240:
1.1.1.4 root 241: case 1:
1.1 root 242: WriteCmd(data);
243: return 0;
1.1.1.4 root 244:
245: case 2:
1.1.1.10! root 246: {
! 247: // ドライブコントロールレジスタ
! 248: putlog(1, "Drive Control <- $%02x", data);
! 249:
! 250: // FDD 割り込みクリア(要実機確認)
! 251: gPEDEC->NegateINT(fdd_vector[0]);
! 252:
! 253: uint8 bit = 0x01;
! 254: for (const auto f : fdd_vector) {
! 255: if (f && bit_falling(drivectrl, data, bit)) {
! 256: f->SetDriveCtrl(data & 0xe0);
! 257: }
! 258: bit <<= 1;
1.1 root 259: }
1.1.1.10! root 260: drivectrl = data;
1.1 root 261: return 0;
1.1.1.10! root 262: }
1.1 root 263:
1.1.1.4 root 264: case 3:
1.1.1.10! root 265: {
! 266: bool is2DD = ((data & 0x10) != 0);
! 267: uint accdrive = data & 3;
! 268: bool motor_on_bit_new = data & 0x80;
! 269:
! 270: if (motor_on_bit_new) {
! 271: putlog(1, "Access Drive <- $%02x (MotorOn %s #%d)", data,
! 272: is2DD ? "2DD" : "2HD",
! 273: accdrive);
! 274:
! 275: if (motor_on_bit == false) {
! 276: // MOTOR_ON ビットが立ち上がったら、全ドライブに通知
! 277: // motor_event は止める。
! 278: gScheduler->StopEvent(motor_event);
! 279: for (const auto f : fdd_vector) {
! 280: if (f) {
! 281: f->SetMotorOn(true);
! 282: }
! 283: }
! 284: }
1.1 root 285: } else {
1.1.1.10! root 286: putlog(1, "Access Drive <- $%02x (MotorOff %s)", data,
! 287: is2DD ? "2DD" : "2HD");
! 288:
! 289: // motor_on_bit はドライブセレクトイネーブルの意味も持つ
! 290:
! 291: // 全ドライブの選択を解除
! 292: accdrive = MAX_DRIVE;
! 293:
! 294: if (motor_on_bit) {
! 295: // MOTOR_ON ビットが立ち下がったら、一定時間後に MOTOR_ON
! 296: // 信号をネゲートする。
! 297: gScheduler->RestartEvent(motor_event);
! 298: }
! 299: }
! 300:
! 301: // FDD 割り込みクリア(要実機確認)
! 302: gPEDEC->NegateINT(fdd_vector[0]);
! 303:
! 304: motor_on_bit = motor_on_bit_new;
! 305:
! 306: DriveSelect(accdrive);
! 307: // 全ドライブに通知
! 308: for (const auto f : fdd_vector) {
! 309: if (f) {
! 310: f->SetDriveSelect(accdrive, is2DD);
! 311: }
1.1 root 312: }
313: return 0;
1.1.1.10! root 314: }
1.1 root 315: }
1.1.1.4 root 316: __unreachable();
1.1 root 317: }
318:
319: uint64
1.1.1.7 root 320: FDCDevice::Peek(uint32 offset)
1.1 root 321: {
1.1.1.10! root 322: switch (offset) {
! 323: case 0:
! 324: if (dack == false) {
! 325: return GetMSR();
! 326: }
! 327: FALLTHROUGH;
! 328:
! 329: case 1:
! 330: {
! 331: // 副作用なくデータを読み出す
! 332: switch (phase) {
! 333: case PHASE_COMMAND:
! 334: return 0xff;
! 335:
! 336: case PHASE_EXEC:
! 337: if (dreg.Empty()) {
! 338: return 0xff; // XXX 何が読めるか
! 339: }
! 340: return dreg.Peek(0);
! 341:
! 342: case PHASE_RESULT:
! 343: return resbuf.Peek();
! 344:
! 345: default:
! 346: __unreachable();
! 347: }
! 348: break;
! 349: }
! 350:
! 351: case 2:
! 352: return GetDriveStatus();
! 353:
! 354: case 3:
! 355: return 0xff;
! 356:
! 357: default:
! 358: __unreachable();
! 359: }
1.1 root 360: }
361:
1.1.1.10! root 362: // モニタ更新
! 363: void
! 364: FDCDevice::MonitorUpdate(Monitor *, TextScreen& screen)
! 365: {
! 366: int x;
! 367: int y;
! 368: uint8 local_msr;
! 369: uint8 local_st0;
! 370: uint8 local_st1;
! 371: uint8 local_st2;
! 372: uint8 drivestat;
! 373: FDDDevice *f;
! 374:
! 375: local_msr = GetMSR();
! 376: local_st0 = GetST0();
! 377: local_st1 = GetST1();
! 378: local_st2 = GetST2();
! 379: drivestat = GetDriveStatus();
! 380:
! 381: screen.Clear();
! 382: y = 0;
! 383:
! 384: // 0 1 2 3 4 5 6
! 385: // 01234567890123456789012345678901234567890123456789012345678901234567890
! 386: // $ee99xx MSR:$00 --
! 387: // ST0:
! 388: // Command: $00 $11 $22 $33 $44 $55 $66 $77 $88 $99 (READ DATA)
! 389:
! 390: // MSR
! 391: screen.Print(0, y, "$%06x MSR:$%02x", (baseaddr + 1), local_msr);
! 392: static const char * const msr_bits[] = {
! 393: "RQM", "DIO", "NDM", "CB", "D3B", "D2B", "D1B", "D0B"
! 394: };
! 395: MonitorReg(screen, 17, y, local_msr, msr_bits);
! 396: y++;
! 397:
! 398: // ST0
! 399: screen.Print(9, y, "ST0:$%02x", local_st0);
! 400: static const char * const st0_bits[] = {
! 401: "", "", "SE", "EC", "NR", "HD", "US1", "US0"
! 402: };
! 403: MonitorReg(screen, 17, y, local_st0, st0_bits);
! 404: static const char * const ic_str[] = {
! 405: "NT",
! 406: "AT",
! 407: "IC",
! 408: "AI",
! 409: };
! 410: screen.Print(17, y, "IC=%s", ic_str[local_st0 >> 6]);
! 411: y++;
! 412:
! 413: // ST1
! 414: screen.Print(9, y, "ST1:$%02x", local_st1);
! 415: static const char * const st1_bits[] = {
! 416: "EN", "-", "DE", "OR", "-", "ND", "NW", "MA"
! 417: };
! 418: MonitorReg(screen, 17, y, local_st1, st1_bits);
! 419: y++;
! 420:
! 421: // ST2
! 422: screen.Print(9, y, "ST2:$%02x", local_st2);
! 423: static const char * const st2_bits[] = {
! 424: "-", "CM", "DD", "NC", "SH", "SN", "BC", "MD"
! 425: };
! 426: MonitorReg(screen, 17, y, local_st2, st2_bits);
! 427: y++;
! 428:
! 429: // ドライブコントロール
! 430: screen.Print(0, y, "$e94005 CTRL:$%02x", drivectrl);
! 431: static const char * const dctrl_bits[] = {
! 432: "LED", "EJM", "EJO", "-", "D#3", "D#2", "D#1", "D#0"
! 433: };
! 434: MonitorReg(screen, 17, y, drivectrl, dctrl_bits);
! 435: y++;
! 436:
! 437: // ドライブステータス
! 438: screen.Print(0, y, " STAT:$%02x", drivestat);
! 439: static const char * const dstat_bits[] = {
! 440: "DIN", "ERR", "-", "-", "-", "-", "-", "-"
! 441: };
! 442: MonitorReg(screen, 17, y, drivestat, dstat_bits);
! 443: y++;
! 444:
! 445: x = 52;
! 446: y = 0;
! 447: screen.Print(x, y++, "HD=%d", hd);
! 448: screen.Print(x, y++, "US=%d", us);
! 449: screen.Print(x, y++, "TC=%d", tc);
! 450: screen.Print(x, y++, "DACK=%d", dack);
! 451:
! 452: x = 60;
! 453: y = 0;
! 454: screen.Print(x, y++, "srt=%d (seek=%d)", srt, seek_srt);
! 455: screen.Print(x, y++, "hut=%d", hut);
! 456: screen.Print(x, y++, "hlt=%d", hlt);
! 457: screen.Print(x, y++, "nd=%d", nd);
! 458:
! 459: x = 52;
! 460: y = 5;
! 461: screen.Puts(x, y, "<OPM>");
! 462: screen.Print(x + 6, y, TA::OnOff(force_ready), "CT2");
! 463:
! 464: // フェーズ、コマンド
! 465: x = 0;
! 466: y = 7;
! 467: static const char * const phase_str[] = {
! 468: "Command",
! 469: "Execute",
! 470: "Result",
! 471: };
! 472: screen.Print(x, y++, "Phase: %s", phase_str[(int)phase]);
! 473: if (cmdbuf.len > 0) {
! 474: screen.Puts(x, y, "Command:");
! 475: for (int i = 0; i < cmdbuf.len; i++) {
! 476: screen.Print(x + 9 + i * 4, y, "$%02x", cmdbuf.buf[i]);
! 477: }
! 478: screen.Print(x + 49, y, "(%s)", cmd->name);
! 479: }
! 480:
! 481: x = 1;
! 482: y = 10;
! 483: // 存在するかに関わらず 4台分表示する (LUNA は知らん)
! 484: screen.Puts(0, y, "<FDD> PCN NCN Ready Poll");
! 485: screen.Puts(26, y, "Motor AccLED Eject FMT C H R N");
! 486: y++;
! 487: for (int i = 0; i < MAX_DRIVE; i++) {
! 488: UnitInfo& u = unitinfo[i];
! 489:
! 490: if (i < fdd_vector.size()) {
! 491: f = fdd_vector[i];
! 492: } else {
! 493: f = NULL;
! 494: }
! 495:
! 496: screen.Print(x, y, "#%d", i);
! 497: if (f) {
! 498: screen.Print(x + 5, y, "%3d %3d", u.pcn, u.ncn);
! 499: screen.Print(x + 13, y, TA::OnOff(u.ready), "Ready");
! 500: static const char * state_str[] = {
! 501: "Idle",
! 502: "Seek",
! 503: "Recal",
! 504: };
! 505: if ((uint)u.state < countof(state_str)) {
! 506: screen.Puts(x + 19, y, state_str[(int)u.state]);
! 507: } else {
! 508: screen.Puts(x + 19, y, "state?");
! 509: }
! 510: } else {
! 511: screen.Puts(x + 5, y, TA::Disable, "Not connected");
! 512: }
! 513:
! 514: y++;
! 515: }
! 516:
! 517: // FDD の状況も同じモニタ内に表示する。
! 518: for (int i = 0; i < fdd_vector.size(); i++) {
! 519: f = fdd_vector[i];
! 520: if (f) {
! 521: f->MonitorUpdateFDD(screen, hd);
! 522: }
! 523: }
! 524: }
! 525:
! 526: // レジスタをビットごとに表示する。MonitorUpdate の下請け。
! 527: void
! 528: FDCDevice::MonitorReg(TextScreen& screen,
! 529: int x, int y, uint8 reg, const char * const *names)
! 530: {
! 531: for (int i = 0; i < 8; i++) {
! 532: if (names[i][0] == '-') {
! 533: screen.Puts(x + i * 4, y, TA::Disable, "---");
! 534: } else {
! 535: bool b = reg & (1 << (7 - i));
! 536: screen.Puts(x + i * 4, y, TA::OnOff(b), names[i]);
! 537: }
! 538: }
! 539: }
! 540:
! 541: // メインステータスレジスタ(MSR)取得
! 542: uint8
! 543: FDCDevice::GetMSR() const
1.1 root 544: {
545: uint8 stat = 0;
546:
1.1.1.10! root 547: if (msr.rqm) {
! 548: stat |= MSR_RQM;
1.1 root 549: }
1.1.1.10! root 550: if (msr.dio == DIOtoHost) {
! 551: stat |= MSR_DIO;
1.1 root 552: }
1.1.1.10! root 553: if (msr.ndm) {
! 554: stat |= MSR_NDM;
1.1 root 555: }
1.1.1.10! root 556: if (msr.cb) {
! 557: stat |= MSR_CB;
1.1 root 558: }
1.1.1.10! root 559: for (int i = 0; i < msr.db.size(); i++) {
! 560: stat |= (msr.db[i] ? 1 : 0) << i;
1.1 root 561: }
562:
563: return stat;
564: }
565:
1.1.1.10! root 566: // ST0 レジスタ取得
! 567: uint8
! 568: FDCDevice::GetST0() const
! 569: {
! 570: uint8 data;
! 571:
! 572: data = st0.ic;
! 573: if (st0.se) {
! 574: data |= ST0_SE;
! 575: }
! 576: if (st0.ec) {
! 577: data |= ST0_EC;
! 578: }
! 579: if (st0.nr) {
! 580: data |= ST0_NR;
! 581: }
! 582: if (st0.hd) {
! 583: data |= ST0_HD;
! 584: }
! 585: data |= st0.us;
! 586:
! 587: return data;
! 588: }
! 589:
! 590: // ST1 レジスタ取得
! 591: uint8
! 592: FDCDevice::GetST1() const
! 593: {
! 594: uint8 data = 0;
! 595:
! 596: if (st1.en) {
! 597: data |= ST1_EN;
! 598: }
! 599: if (st1.de) {
! 600: data |= ST1_DE;
! 601: }
! 602: if (st1.overrun) {
! 603: data |= ST1_OR;
! 604: }
! 605: if (st1.nd) {
! 606: data |= ST1_ND;
! 607: }
! 608: if (st1.nw) {
! 609: data |= ST1_NW;
! 610: }
! 611: if (st1.ma) {
! 612: data |= ST1_MA;
! 613: }
! 614: return data;
! 615: }
! 616:
! 617: // ST2 レジスタ取得
! 618: uint8
! 619: FDCDevice::GetST2() const
! 620: {
! 621: uint8 data = 0;
! 622:
! 623: if (st2.cm) {
! 624: data |= ST2_CM;
! 625: }
! 626: if (st2.dd) {
! 627: data |= ST2_DD;
! 628: }
! 629: if (st2.nc) {
! 630: data |= ST2_NC;
! 631: }
! 632: if (st2.sh) {
! 633: data |= ST2_SH;
! 634: }
! 635: if (st2.sn) {
! 636: data |= ST2_SN;
! 637: }
! 638: if (st2.bc) {
! 639: data |= ST2_BC;
! 640: }
! 641: if (st2.md) {
! 642: data |= ST2_MD;
! 643: }
! 644: return data;
! 645: }
! 646:
1.1 root 647: // FDC データレジスタ読み込み
648: uint32
649: FDCDevice::ReadData()
650: {
651: uint8 data;
652:
1.1.1.10! root 653: // TODO: RQM, DIO 処理
! 654:
! 655: switch (phase) {
! 656: case PHASE_COMMAND:
! 657: // 規定フェーズ外の読み込み
! 658: // XXX ほんと?
! 659: putlog(1, "Read DATA: in irregular phase");
! 660: data = 0xff;
! 661: return data;
! 662:
! 663: case PHASE_EXEC:
! 664: if (dreg.Dequeue(&data) == false) {
! 665: // エラー
! 666: return 0xff;
! 667: }
! 668: putlog(4, "DATA -> $%02x", data);
! 669: return data;
! 670:
! 671: case PHASE_RESULT:
! 672: data = resbuf.Take();
! 673: putlog(2, "DATA -> $%02x", data);
! 674: if (resbuf.pos == 1) {
! 675: // 1 バイトを読んだところで
! 676: // ステータスをレポートするとクリアされるようだ
! 677: st0.ic = ST0_IC_IC;
! 678: st0.se = false;
! 679: st0.ec = false;
! 680: st0.nr = false;
! 681: st0.hd = false;
! 682: st0.us = 0;
! 683: ChangeInterrupt();
! 684: }
! 685: if (resbuf.pos == resbuf.len) {
! 686: CommandPhase();
! 687: }
! 688: return data;
! 689:
! 690: default:
! 691: VMPANIC("corrupted phase=$%x", (int)phase);
1.1 root 692: }
1.1.1.10! root 693: __unreachable();
! 694: }
1.1 root 695:
1.1.1.10! root 696: // ドライブステータスレジスタ取得
! 697: uint32
! 698: FDCDevice::GetDriveStatus() const
! 699: {
! 700: uint8 data = 0;
! 701:
! 702: // ドライブコントロールのドライブ選択が %1 になっているドライブを調べる。
! 703: // 誤挿入は未対応。
! 704: uint8 bit = 0x01;
! 705: for (const auto f : fdd_vector) {
! 706: if ((drivectrl & bit)) {
! 707: if (f && f->IsMediumLoaded()) {
! 708: data |= 0x80;
! 709: }
! 710: }
! 711: bit <<= 1;
1.1 root 712: }
1.1.1.10! root 713:
1.1 root 714: return data;
715: }
716:
717: // ドライブステータスレジスタ読み込み
718: uint32
719: FDCDevice::ReadDriveStatus()
720: {
1.1.1.10! root 721: uint8 data = GetDriveStatus();
! 722: putlog(1, "Drive Status -> $%02x", data);
1.1 root 723: return data;
724: }
725:
726: // FDC コマンドレジスタ書き込み
727: void
728: FDCDevice::WriteCmd(uint32 data)
729: {
1.1.1.10! root 730: switch (phase) {
! 731: case PHASE_COMMAND:
! 732: if (msr.rqm == false) {
! 733: // リクエストしていないコマンド書き込み
! 734: putlog(1, "CMD <- $%02x: not requested", data);
1.1 root 735: break;
1.1.1.10! root 736: }
! 737: msr.rqm = false;
1.1 root 738:
1.1.1.10! root 739: putlog(2, "CMD[%d] <- $%02x", cmdbuf.len, data);
! 740: cmdbuf.buf[cmdbuf.len++] = data;
! 741: // コマンドの1バイト目でディスパッチ
! 742: if (cmdbuf.len == 1) {
! 743: // 見付からなければリスト末尾の Invalid にマッチする
! 744: for (auto& c : cmd_list) {
! 745: if ((cmdbuf.code & c.mask) == c.code) {
! 746: cmd = &c;
! 747: break;
! 748: }
! 749: }
! 750: putlog(1, "Command %s accepted", cmd->name);
1.1 root 751: }
1.1.1.10! root 752: CallAfter(CommandCallback, 2_usec);
! 753: break;
! 754:
! 755: case PHASE_EXEC:
! 756: if (msr.dio == DIOtoFDC) {
! 757: WriteDataReg(data);
! 758: } else {
! 759: putlog(1, "CMD <- $%02x: unexpected data write (DIO)", data);
! 760: }
! 761: break;
! 762:
! 763: case PHASE_RESULT:
! 764: putlog(1, "CMD <- $%02x: unexpected data write (result phase)", data);
! 765: break;
1.1 root 766:
1.1.1.10! root 767: default:
! 768: VMPANIC("corrupted phase=$%x", (int)phase);
! 769: }
! 770: }
! 771:
! 772: // FDC データレジスタ書き込み
! 773: void
! 774: FDCDevice::WriteDataReg(uint32 data)
! 775: {
! 776: dreg.EnqueueForce(data);
! 777: }
! 778:
! 779: // Command フェーズに移行
! 780: void
! 781: FDCDevice::CommandPhase()
! 782: {
! 783: putlog(2, "Command Phase");
! 784: phase = PHASE_COMMAND;
! 785: msr.rqm = true;
! 786: msr.dio = DIOtoFDC;
! 787: msr.ndm = false;
! 788: msr.cb = false;
! 789: cmdbuf.Clear();
! 790: resbuf.Clear();
! 791: }
! 792:
! 793: // Exec フェーズに移行
! 794: void
! 795: FDCDevice::ExecPhase()
! 796: {
! 797: putlog(2, "Exec Phase: %s", cmd->name);
! 798: phase = PHASE_EXEC;
! 799: msr.cb = true;
! 800: if (nd) {
! 801: msr.ndm = true; // XXX ?
! 802: }
! 803: }
! 804:
! 805: // Result フェーズに移行
! 806: void
! 807: FDCDevice::ResultPhase()
! 808: {
! 809: putlog(2, "Result Phase");
! 810: phase = PHASE_RESULT;
! 811: msr.rqm = true;
! 812: msr.dio = DIOtoHost;
! 813: msr.ndm = false;
! 814: msr.cb = true;
! 815: }
! 816:
! 817: void
! 818: FDCDevice::ResultCallback(Event& ev)
! 819: {
! 820: ResultPhase();
! 821: }
! 822:
! 823: void
! 824: FDCDevice::CommandCallback(Event& ev)
! 825: {
! 826: msr.cb = true;
! 827: if (cmdbuf.len == cmd->cmdlen) {
! 828: putlog(2, "Command %s execute", cmd->name);
! 829: (this->*(cmd->func))();
1.1 root 830: } else {
1.1.1.10! root 831: msr.rqm = true;
1.1 root 832: }
1.1.1.10! root 833: }
1.1 root 834:
1.1.1.10! root 835: // 割り込み信号線の状態を変える
! 836: void
! 837: FDCDevice::ChangeInterrupt()
! 838: {
! 839: if (GetST0() != 0x80 || (msr.ndm && 0/* XXX DRQ*/)) {
! 840: putlog(4, "AssertINT");
! 841: gPEDEC->AssertINT(this);
! 842: } else {
! 843: putlog(4, "NegateINT");
! 844: gPEDEC->NegateINT(this);
1.1 root 845: }
846: }
847:
1.1.1.10! root 848: // READY 信号が変化したことの通知 (FDD から呼ばれる)
1.1 root 849: void
1.1.1.10! root 850: FDCDevice::ReadyChanged(int unit, bool is_ready)
1.1 root 851: {
1.1.1.10! root 852: // 実際は READY 信号は SPECIFY コマンドが呼ばれた後 1.024 msec 間隔で
! 853: // 常にポーリングしているが、エミュレータでは無駄なので、変化があった
! 854: // ことの通知を受けてポーリングループを起動し、どのユニットも変化が
! 855: // なくなったらポーリングを止めるようにしておく。
! 856: // uPD765 マニュアル POLLING FEATURE OF THE uPD765 (p.433)
! 857:
! 858: // SPECIFY コマンドで有効になる
! 859: if (initialized) {
! 860: StartPoll();
! 861: }
! 862: // 転送の同期を取るために、READY になった時刻を覚えておく
! 863: if (is_ready) {
! 864: unitinfo[unit].ready_time = gScheduler->GetVirtTime();
! 865: }
1.1 root 866: }
867:
1.1.1.10! root 868: // ポーリング開始
1.1 root 869: void
1.1.1.10! root 870: FDCDevice::StartPoll()
1.1 root 871: {
1.1.1.10! root 872: if (poll_event.IsRunning() == false) {
! 873: putlog(3, "Start polling");
! 874: gScheduler->StartEvent(poll_event);
! 875: }
! 876: }
1.1 root 877:
1.1.1.10! root 878: // ポーリングイベントコールバック
! 879: void
! 880: FDCDevice::PollEventCallback(Event& ev)
! 881: {
! 882: bool is_poll = false;
1.1 root 883:
1.1.1.10! root 884: // READY ポーリング。
! 885: // コマンドフェーズにいてコマンド未発行のときだけ実行。
! 886: // Inside で言う「シーク中の E-PHASE」も FDC としては
! 887: // コマンドフェーズなので同じ状態になっている。
! 888: if (phase == PHASE_COMMAND && cmdbuf.len == 0) {
! 889: for (int unit_no = 0; unit_no < MAX_DRIVE; unit_no++) {
! 890: is_poll |= PollReady(unit_no);
1.1.1.9 root 891: }
1.1.1.10! root 892: }
1.1 root 893:
1.1.1.10! root 894: // シーク
! 895: seek_srt++;
! 896: if (seek_srt == 16) {
! 897: seek_srt = srt;
! 898: for (int unit_no = 0; unit_no < MAX_DRIVE; unit_no++) {
! 899: is_poll |= ExecSeek(unit_no);
! 900: }
! 901: } else {
! 902: is_poll = true;
! 903: }
1.1 root 904:
1.1.1.10! root 905: if (is_poll) {
! 906: gScheduler->RestartEvent(ev);
! 907: } else {
! 908: putlog(3, "Stop polling");
! 909: }
! 910: }
1.1 root 911:
1.1.1.10! root 912: // READY ポーリングの実行。
! 913: // ポーリングを継続する必要があるときは true を返す。
! 914: bool
! 915: FDCDevice::PollReady(int unit_no)
! 916: {
! 917: auto& unit = unitinfo[unit_no];
! 918:
! 919: UnitSelect(unit_no);
! 920: bool r = IsReady();
! 921: if (unit.ready != r) {
! 922: // READY が変化したので、ドライブ状態が Idle でかつ、
! 923: // 割り込み発行中でなければ状態を更新して割り込みを発行
! 924: if (unit.state == UnitState::Idle && st0.ic == ST0_IC_IC) {
! 925: unit.ready = r;
! 926: st0.ic = ST0_IC_AI;
! 927: st0.nr = !r;
! 928: st0.us = unit_no;
! 929: ChangeInterrupt();
! 930: return false;
! 931: } else {
! 932: // 割り込み発生をペンディングしているのでポーリング続行
! 933: return true;
! 934: }
1.1 root 935: }
1.1.1.10! root 936: return false;
1.1 root 937: }
938:
1.1.1.10! root 939: // リキャリブレートまたはシークの実行。
! 940: // ポーリングを継続する必要があるときは true を返す。
! 941: bool
! 942: FDCDevice::ExecSeek(int unit_no)
! 943: {
! 944: auto& unit = unitinfo[unit_no];
! 945:
! 946: if (unit.state == UnitState::Idle) {
! 947: return false;
! 948: }
! 949:
! 950: UnitSelect(unit_no);
! 951:
! 952: if (IsReady() == false) {
! 953: unit.state = UnitState::Idle;
! 954: st0.nr = true;
! 955: st0.ic = ST0_IC_AT;
! 956: st0.hd = false;
! 957: st0.us = us;
! 958: ChangeInterrupt();
! 959: CommandPhase();
! 960: return false;
! 961: }
! 962: unit.ready = true;
! 963:
! 964: if (unit.state == UnitState::Recalibrate) {
! 965: if (fdd && fdd->GetTrack0()) {
! 966: putlog(2, "Track0 detected");
! 967: unit.pcn = 0;
! 968: SeekEnd();
! 969: return false;
! 970: } else if (unit.pcn < 0) {
! 971: putlog(2, "Track0 not detected");
! 972: st0.ec = true;
! 973: SeekEnd();
! 974: return false;
! 975: }
! 976: } else {
! 977: if (unit.pcn == unit.ncn) {
! 978: SeekEnd();
! 979: return false;
! 980: }
! 981: }
! 982:
! 983: // シークを FDD に発行
! 984: int dir;
! 985: if (unit.pcn < unit.ncn) {
! 986: dir = 1;
! 987: } else {
! 988: dir = -1;
! 989: }
! 990: unit.pcn += dir;
! 991: if (fdd) {
! 992: fdd->DoSeek(dir);
! 993: }
! 994: return true;
! 995: }
! 996:
! 997: // 強制レディ信号の設定 (OPM から呼ばれる)
! 998: void
! 999: FDCDevice::SetForceReady(bool force_ready_)
! 1000: {
! 1001: force_ready = force_ready_;
! 1002: putlog(2, "Force Ready %s", force_ready ? "On" : "Off");
! 1003: if (initialized) {
! 1004: StartPoll();
! 1005: }
! 1006: }
! 1007:
! 1008: // DACK 信号をアサートする (DMAC から呼ばれる)
1.1 root 1009: void
1.1.1.10! root 1010: FDCDevice::AssertDACK(bool tc_)
1.1 root 1011: {
1.1.1.10! root 1012: dack = true;
! 1013: tc = tc_;
! 1014: // DRQ を下げる
! 1015: gDMAC->NegateREQ(0);
1.1 root 1016: }
1017:
1.1.1.10! root 1018: // DACK 信号をネゲートする (DMAC から呼ばれる)
1.1 root 1019: void
1.1.1.10! root 1020: FDCDevice::NegateDACK()
1.1 root 1021: {
1.1.1.10! root 1022: dack = false;
! 1023: }
! 1024:
! 1025: // 内部データレジスタに data を投入
! 1026: void
! 1027: FDCDevice::PushDreg(uint8 data)
! 1028: {
! 1029: if (dreg.Enqueue(data)) {
! 1030: // FDC DRQ をアサートして DMAC REQ#0 を送る
! 1031: RequestDataByte();
! 1032: } else {
! 1033: // OverRun
! 1034: putlog(1, "PushDreg overrun detected");
! 1035: st1.overrun = true;
! 1036: st0.ic = ST0_IC_AT;
! 1037: ChangeInterrupt();
! 1038: }
! 1039: }
! 1040:
! 1041: // 内部データレジスタから data を取り出す。
! 1042: // 空なら -1 を返す。
! 1043: int
! 1044: FDCDevice::PopDreg()
! 1045: {
! 1046: uint8 data;
! 1047:
! 1048: if (dreg.Dequeue(&data)) {
! 1049: return data;
! 1050: } else {
! 1051: // OverRun
! 1052: putlog(1, "PopDreg overrun detected");
! 1053: st1.overrun = true;
! 1054: st0.ic = ST0_IC_AT;
! 1055: ChangeInterrupt();
! 1056: return -1;
1.1 root 1057: }
1058: }
1059:
1.1.1.10! root 1060: // CPU または DMAC に読み書きデータを要求する
1.1 root 1061: void
1.1.1.10! root 1062: FDCDevice::RequestDataByte()
1.1 root 1063: {
1.1.1.10! root 1064: if (nd) {
! 1065: putlog(0, "CPU Transfer mode (NOT IMPLEMENTED)");
! 1066: } else {
! 1067: gDMAC->AssertREQ(0);
! 1068: }
1.1.1.9 root 1069: }
1070:
1.1.1.10! root 1071: // ユニット US0,US1 信号線の状態を変える。
1.1.1.9 root 1072: void
1.1.1.10! root 1073: FDCDevice::UnitSelect(uint8 us_)
1.1.1.9 root 1074: {
1.1.1.10! root 1075: // こちらは FDC 側の US0, US1 信号線の出力に相当。
! 1076: // アクセスドライブは X68k では I/O コントローラ側で決まる。
! 1077: // LUNA では XP 側の PSG の I/O と US 信号線の組み合わせで決まる。
! 1078: us = us_ & 0x03;
! 1079:
! 1080: putlog(3, "UnitSelect US:%d", us);
! 1081:
! 1082: // いまのところ LUNA のことは忘れる
1.1 root 1083: }
1084:
1.1.1.10! root 1085: // ドライブセレクト信号線の状態を変える。
! 1086: void
! 1087: FDCDevice::DriveSelect(uint8 drive)
! 1088: {
! 1089: // こちらは I/O コントローラにあるアクセスドライブセレクトのほう
! 1090: if (drive < ndrive) {
! 1091: fdd = fdd_vector[drive];
! 1092: } else {
! 1093: fdd = NULL;
! 1094: }
! 1095:
! 1096: // いまのところ LUNA のことは忘れる
! 1097: }
! 1098:
! 1099: // 一定時間後に MOTOR_ON 信号をネゲートするイベント。
! 1100: // SED9420 の TMOUT 信号に相当。
! 1101: void
! 1102: FDCDevice::MotorEventCallback(Event& ev)
! 1103: {
! 1104: // 全ドライブに通知
! 1105: for (const auto f : fdd_vector) {
! 1106: if (f) {
! 1107: f->SetMotorOn(false);
! 1108: }
! 1109: }
! 1110: }
! 1111:
! 1112: // FDC コマンド
! 1113:
! 1114: void
! 1115: FDCDevice::CmdReadDiagnostic()
! 1116: {
! 1117: CmdNotImplemented();
! 1118: }
! 1119:
! 1120: void
! 1121: FDCDevice::CmdSpecify()
! 1122: {
! 1123: srt = cmdbuf.specify.srt_hut >> 4;
! 1124: hut = cmdbuf.specify.srt_hut & 15;
! 1125: hlt = cmdbuf.specify.hlt_nd >> 1;
! 1126: nd = cmdbuf.specify.hlt_nd & 1;
! 1127: putlog(2, " SRT:%d HUT:%d HLT:%d ND:%d", srt, hut, hlt, nd);
! 1128:
! 1129: // 初期化完了、ポーリング開始
! 1130: initialized = true;
! 1131:
! 1132: CommandPhase();
! 1133: }
! 1134:
! 1135: void
! 1136: FDCDevice::CmdSenseDeviceStatus()
! 1137: {
! 1138: UnitSelect(cmdbuf.sense_device_status.hd_us);
! 1139:
! 1140: uint8 st3 = 0;
! 1141: // FT = fdd.GetFault();
! 1142: if (fdd && fdd->GetWriteProtect()) {
! 1143: st3 |= ST3_WP;
! 1144: }
! 1145: if (IsReady()) {
! 1146: st3 |= ST3_RY;
! 1147: }
! 1148: if (fdd && fdd->GetTrack0()) {
! 1149: st3 |= ST3_T0;
! 1150: }
! 1151: // TS = fdd.GetTwoSide();
! 1152: // HD, US
! 1153: st3 |= (cmdbuf.sense_device_status.hd_us & 0x07);
! 1154:
! 1155: putlog(2, " ST3=$%02x", st3);
! 1156: resbuf.device_status.st3 = st3;
! 1157: resbuf.len = sizeof(resbuf.device_status);
! 1158: // 1.024 msec は READY のポーリング間隔から推定。
! 1159: // 正確な数字は実機調査すること。
! 1160: CallAfter(ResultCallback, 1024_usec);
! 1161: }
! 1162:
! 1163: void
! 1164: FDCDevice::CmdWriteData()
! 1165: {
! 1166: UnitSelect(cmdbuf.data.hd_us);
! 1167:
! 1168: mt = cmdbuf.code & 0x80;
! 1169: mf = cmdbuf.code & 0x40;
! 1170: sk = 0;
! 1171:
! 1172: hd = (cmdbuf.data.hd_us & 0x04) >> 2;
! 1173:
! 1174: if (loglevel >= 2) {
! 1175: putlogn(" MT:%d MF:%d SK:%d HD:%d", (int)mt, (int)mf, (int)sk, hd);
! 1176: putlogn(" C:%2d H:%d R:%2d N:%d",
! 1177: cmdbuf.data.c,
! 1178: cmdbuf.data.h,
! 1179: cmdbuf.data.r,
! 1180: cmdbuf.data.n);
! 1181: putlogn(" EOT:%2d GSL:$%02x DTL:$%02x",
! 1182: cmdbuf.data.eot,
! 1183: cmdbuf.data.gsl,
! 1184: cmdbuf.data.dtl);
! 1185: }
! 1186:
! 1187: XferStart(true);
! 1188: }
! 1189:
! 1190: void
! 1191: FDCDevice::CmdReadData()
! 1192: {
! 1193: UnitSelect(cmdbuf.data.hd_us);
! 1194:
! 1195: mt = cmdbuf.code & 0x80;
! 1196: mf = cmdbuf.code & 0x40;
! 1197: sk = cmdbuf.code & 0x20;
! 1198:
! 1199: hd = (cmdbuf.data.hd_us & 0x04) >> 2;
! 1200:
! 1201: if (loglevel >= 2) {
! 1202: putlogn(" MT:%d MF:%d SK:%d HD:%d", (int)mt, (int)mf, (int)sk, hd);
! 1203: putlogn(" C:%2d H:%d R:%2d N:%d",
! 1204: cmdbuf.data.c,
! 1205: cmdbuf.data.h,
! 1206: cmdbuf.data.r,
! 1207: cmdbuf.data.n);
! 1208: putlogn(" EOT:%2d GSL:$%02x DTL:$%02x",
! 1209: cmdbuf.data.eot,
! 1210: cmdbuf.data.gsl,
! 1211: cmdbuf.data.dtl);
! 1212: }
! 1213:
! 1214: XferStart(false);
! 1215: }
! 1216:
! 1217: void
! 1218: FDCDevice::XferStart(bool is_write)
! 1219: {
! 1220: resbuf.len = sizeof(resbuf.data);
! 1221:
! 1222: if (IsReady() == false) {
! 1223: st0.nr = true;
! 1224: st0.ic = ST0_IC_AT;
! 1225: CallAfter(XferEndCallback, 0);
! 1226: return;
! 1227: }
! 1228: unitinfo[us].ready = true;
! 1229: assert(fdd);
! 1230:
! 1231: tc = false;
! 1232: dack = false;
! 1233: dreg.Clear();
! 1234: xfer_start = false;
! 1235: xfer_enable = false;
! 1236: xfer_write = is_write;
! 1237: index_detected = 0;
! 1238:
! 1239: fdd->ReadTrack(hd);
! 1240:
! 1241: ExecPhase();
! 1242:
! 1243: // FDD がバイトを読み書きするちょうど真ん中のタイミングで動くように
! 1244: // 同期してみる。
! 1245: uint64 now = gScheduler->GetVirtTime();
! 1246: uint64 t = 16_usec - ((now - unitinfo[us].ready_time) % 16_usec);
! 1247: t += 8_usec;
! 1248:
! 1249: CallAfter(XferStartCallback, t);
! 1250: }
! 1251:
! 1252: void
! 1253: FDCDevice::XferStartCallback(Event& ev)
! 1254: {
! 1255: // マーク位置まで待つ。
! 1256: // マークを読んでみてディスパッチ。
! 1257: // IAM ならなにもしない。
! 1258: // IDAM なら CHRN を読み出す。データ転送が開始されていない時は
! 1259: // R を検査して、一致したら転送開始状態にしておく。
! 1260: // DAM/DDAM のときは、転送開始状態でなければ何もしない。
! 1261: // 転送開始状態の時はデータ転送またはエラー処理。
! 1262: // 転送が終わったら CHR を進めて EOT になったら
! 1263: // MT=1 で HD=0 のときは裏に行く。
! 1264: // そうでなければ EN エラー。
! 1265:
! 1266: assert(fdd);
! 1267:
! 1268: int dist = fdd->GetDistanceToMark();
! 1269: if (dist < 0) {
! 1270: VMPANIC("dist < 0");
! 1271: }
! 1272: if (dist == 0) {
! 1273: uint8 mark = fdd->ReadByte();
! 1274: ev.time = 1 * 16_usec;
! 1275: if (mark == MARK_IDAM) {
! 1276: // CHRN と CRC の合計時間を加算
! 1277: ev.time += (4 + 2) * 16_usec;
! 1278:
! 1279: resbuf.data.c = fdd->PeekByte(1);
! 1280: resbuf.data.h = fdd->PeekByte(2);
! 1281: resbuf.data.r = fdd->PeekByte(3);
! 1282: resbuf.data.n = fdd->PeekByte(4);
! 1283: putlog(2, "IDAM C:%2d H:%d R:%2d N:%d",
! 1284: resbuf.data.c,
! 1285: resbuf.data.h,
! 1286: resbuf.data.r,
! 1287: resbuf.data.n);
! 1288:
! 1289: // READ ID は最初に発見した ID を返す。
! 1290: if ((cmdbuf.code & 0xbf) == 0x0a) {
! 1291: putlog(2, "READ ID complete");
! 1292: st0.ic = ST0_IC_NT;
! 1293: CallAfter(XferEndCallback, ev.time);
! 1294: return;
! 1295: }
! 1296:
! 1297: // uPD765 マニュアル pXX に R だけを一致検査すると書いてある。
! 1298: // 72065B でどうかは未調査だが、CHRN 4つ比較することにする。
! 1299: if ((resbuf.data.c == cmdbuf.data.c) &&
! 1300: (resbuf.data.h == cmdbuf.data.h) &&
! 1301: (resbuf.data.r == cmdbuf.data.r) &&
! 1302: (resbuf.data.n == cmdbuf.data.n))
! 1303: {
! 1304: putlog(3, "Transfer decided");
! 1305: xfer_start = true;
! 1306: sect_remain = 1 << (7 + resbuf.data.n);
! 1307: if (cmdbuf.data.n == 0) {
! 1308: xfer_remain = cmdbuf.data.dtl;
! 1309: if (xfer_remain > 128) {
! 1310: xfer_remain = 128;
! 1311: }
! 1312: } else {
! 1313: xfer_remain = sect_remain;
! 1314: }
! 1315: // CRC のぶん
! 1316: sect_remain += 2;
! 1317: }
! 1318: } else if (mark == MARK_DAM) {
! 1319: xfer_enable = xfer_start;
! 1320: if (xfer_enable) {
! 1321: putlog(3, "DAM detected. Sector data will be transferred");
! 1322: if (xfer_write) {
! 1323: RequestDataByte();
! 1324: }
! 1325: CallAfter(XferDataCallback, ev.time);
! 1326: return;
! 1327: } else {
! 1328: putlog(3, "DAM detected. Sector data will be skipped");
! 1329: }
! 1330: } else if (mark == MARK_DDAM) {
! 1331: VMPANIC("DDAM (NOT IMPLEMENTED)");
! 1332: } else if (mark == MARK_IAM) {
! 1333: // IAM は何もすることがない
! 1334: } else {
! 1335: // インデックスホールを都合3回検出したらエラー終了
! 1336: if (fdd->IsIndex()) {
! 1337: index_detected++;
! 1338: if (index_detected == 3) {
! 1339: st0.ic = ST0_IC_AT;
! 1340: if (xfer_start) {
! 1341: st1.nd = true;
! 1342: } else {
! 1343: st1.ma = true;
! 1344: }
! 1345: CallAfter(XferEndCallback, ev.time);
! 1346: return;
! 1347: }
! 1348: }
! 1349: }
! 1350: } else {
! 1351: // 次のマークまで待つ
! 1352: ev.time = dist * 16_usec;
! 1353: }
! 1354: gScheduler->StartEvent(ev);
! 1355: }
! 1356:
! 1357: void
! 1358: FDCDevice::XferDataCallback(Event& ev)
! 1359: {
! 1360: assert(fdd);
! 1361:
! 1362: ev.time = 1 * 16_usec;
! 1363:
! 1364: // 変態フォーマットをサポートするときはこのへんに追加が必要。
! 1365:
! 1366: if (xfer_remain > 0) {
! 1367: if (__predict_false(xfer_write)) {
! 1368: int data = PopDreg();
! 1369: if (data >= 0) {
! 1370: fdd->WriteByte(data);
! 1371: } else {
! 1372: // XXX: こうじゃなくてエラービットだけ立ててセクタ終了時処理かも
! 1373: return;
! 1374: }
! 1375: } else {
! 1376: PushDreg(fdd->ReadByte());
! 1377: }
! 1378: xfer_remain--;
! 1379: }
! 1380:
! 1381: bool is_eot = false;
! 1382:
! 1383: if (sect_remain > 0) {
! 1384: sect_remain--;
! 1385: } else {
! 1386: // セクタデータ転送完了
! 1387: putlog(3, "Sector data transfer completed");
! 1388: // TODO: Data CRC チェック
! 1389:
! 1390: xfer_start = false;
! 1391: xfer_enable = false;
! 1392:
! 1393: is_eot = (resbuf.data.r >= cmdbuf.data.eot);
! 1394:
! 1395: // マルチトラックの処理
! 1396: if (is_eot) {
! 1397: if (xfer_write) {
! 1398: fdd->WriteTrack();
! 1399: }
! 1400: resbuf.data.r = 1;
! 1401: if (mt && hd == 0) {
! 1402: is_eot = false;
! 1403:
! 1404: // HD=1 の読み込み
! 1405: fdd->ReadTrack(1);
! 1406: } else {
! 1407: resbuf.data.c += 1;
! 1408: }
! 1409: if (mt) {
! 1410: // HD=1 から MT を立てて読み終わると HD=0 になっている
! 1411: hd ^= 1;
! 1412: resbuf.data.h = hd;
! 1413: }
! 1414: } else {
! 1415: resbuf.data.r += 1;
! 1416: }
! 1417:
! 1418: // EOT チェック
! 1419: if (tc == false) {
! 1420: if (is_eot) {
! 1421: putlog(3, "EOT reached");
! 1422: st1.en = true;
! 1423: st0.ic = ST0_IC_AT;
! 1424: CallAfter(XferEndCallback, 16_usec);
! 1425: return;
! 1426: } else {
! 1427: // 次のセクタの ID へ継続
! 1428: putlog(3, "Multi sector; continue to transfer");
! 1429: cmdbuf.data.c = resbuf.data.c;
! 1430: cmdbuf.data.h = resbuf.data.h;
! 1431: cmdbuf.data.r = resbuf.data.r;
! 1432: cmdbuf.data.n = resbuf.data.n;
! 1433: CallAfter(XferStartCallback, 16_usec);
! 1434: return;
! 1435: }
! 1436: }
! 1437: }
! 1438:
! 1439: if (tc) {
! 1440: putlog(3, "Done by TC signal");
! 1441: if (xfer_write) {
! 1442: fdd->WriteTrack();
! 1443: }
! 1444: st0.ic = ST0_IC_NT;
! 1445: CallAfter(XferEndCallback, 16_usec);
! 1446: return;
! 1447: }
! 1448:
! 1449: if (xfer_write && xfer_remain > 0) {
! 1450: RequestDataByte();
! 1451: }
! 1452: gScheduler->StartEvent(ev);
! 1453: }
! 1454:
! 1455: void
! 1456: FDCDevice::XferEndCallback(Event& ev)
! 1457: {
! 1458: st0.hd = hd;
! 1459: st0.us = us;
! 1460: resbuf.data.st0 = GetST0();
! 1461: resbuf.data.st1 = GetST1();
! 1462: resbuf.data.st2 = GetST2();
! 1463: ChangeInterrupt();
! 1464: ResultPhase();
! 1465: }
! 1466:
! 1467: // READY 入力端子
! 1468: bool
! 1469: FDCDevice::IsReady() const
! 1470: {
! 1471: return force_ready || (fdd && fdd->GetReady());
! 1472: }
! 1473:
! 1474: void
! 1475: FDCDevice::CmdRecalibrate()
! 1476: {
! 1477: UnitSelect(cmdbuf.recalibrate.us);
! 1478:
! 1479: if (IsReady() == false) {
! 1480: st0.nr = true;
! 1481: st0.ic = ST0_IC_AT;
! 1482: st0.hd = false;
! 1483: st0.us = us;
! 1484: ChangeInterrupt();
! 1485: CommandPhase();
! 1486: return;
! 1487: }
! 1488: unitinfo[us].ready = true;
! 1489:
! 1490: msr.db[us] = true;
! 1491: unitinfo[us].pcn = 255;
! 1492: unitinfo[us].ncn = 0;
! 1493: unitinfo[us].state = UnitState::Recalibrate;
! 1494: CommandPhase();
! 1495: StartPoll();
! 1496: }
! 1497:
! 1498: // シーク終了処理
! 1499: void
! 1500: FDCDevice::SeekEnd()
! 1501: {
! 1502: putlog(2, "Seek completed");
! 1503: st0.ic = ST0_IC_NT;
! 1504: st0.se = true;
! 1505: st0.us = us;
! 1506: unitinfo[us].state = UnitState::Idle;
! 1507: ChangeInterrupt();
! 1508: CommandPhase();
! 1509: }
1.1 root 1510:
1.1.1.10! root 1511: void
! 1512: FDCDevice::CmdSenseInterruptStatus()
1.1 root 1513: {
1.1.1.10! root 1514: // XXX とりあえずここで割り込みを下げる。
! 1515: // 実際には割り込み状態をラッチして信号線に出すというコマンドで
! 1516: // 割り込み要因がオフになっていればそれによってネゲートされる
! 1517: // ということのようだ。
! 1518:
! 1519: // XXX 割り込みが起きていないときは INVALID と同じらしい
! 1520:
! 1521: if (st0.ic != ST0_IC_IC) {
! 1522: // シーク完了割り込みを発生させた Unit のトラックをレポート
! 1523: resbuf.interrupt_status.pcn = unitinfo[st0.us].pcn;
! 1524: msr.db[st0.us] = false;
! 1525: resbuf.len = sizeof(resbuf.interrupt_status);
1.1 root 1526: } else {
1.1.1.10! root 1527: resbuf.len = sizeof(resbuf.invalid);
1.1 root 1528: }
1.1.1.10! root 1529: resbuf.interrupt_status.st0 = GetST0();
! 1530: putlog(2, " ST0=$%02x", resbuf.interrupt_status.st0);
! 1531: ResultPhase();
! 1532: }
! 1533:
! 1534: void
! 1535: FDCDevice::CmdWriteDeletedData()
! 1536: {
! 1537: CmdNotImplemented();
! 1538: }
! 1539:
! 1540: void
! 1541: FDCDevice::CmdReadID()
! 1542: {
! 1543: UnitSelect(cmdbuf.data.hd_us);
! 1544:
! 1545: mf = cmdbuf.code & 0x40;
! 1546: hd = (cmdbuf.data.hd_us & 0x04) >> 2;
! 1547: putlog(2, " MF:%d HD:%d", (int)mf, hd);
! 1548:
! 1549: XferStart(false);
1.1 root 1550: }
1.1.1.10! root 1551:
! 1552: void
! 1553: FDCDevice::CmdReadDeletedData()
! 1554: {
! 1555: CmdNotImplemented();
! 1556: }
! 1557:
! 1558: void
! 1559: FDCDevice::CmdWriteID()
! 1560: {
! 1561: CmdNotImplemented();
! 1562: }
! 1563:
! 1564: void
! 1565: FDCDevice::CmdSeek()
! 1566: {
! 1567: putlog(2, "SEEK start");
! 1568:
! 1569: UnitSelect(cmdbuf.seek.us);
! 1570:
! 1571: if (IsReady() == false) {
! 1572: st0.nr = true;
! 1573: st0.ic = ST0_IC_AT;
! 1574: st0.hd = false;
! 1575: st0.us = us;
! 1576: ChangeInterrupt();
! 1577: CommandPhase();
! 1578: return;
! 1579: }
! 1580: unitinfo[us].ready = true;
! 1581:
! 1582: msr.db[us] = true;
! 1583: unitinfo[us].ncn = cmdbuf.seek.ncn;
! 1584: unitinfo[us].state = UnitState::Seek;
! 1585: putlog(2, " NCN:%d", unitinfo[us].ncn);
! 1586: CommandPhase();
! 1587: StartPoll();
! 1588: }
! 1589:
! 1590: void
! 1591: FDCDevice::CmdVersion()
! 1592: {
! 1593: CmdNotImplemented();
! 1594: }
! 1595:
! 1596: void
! 1597: FDCDevice::CmdScanEqual()
! 1598: {
! 1599: CmdNotImplemented();
! 1600: }
! 1601:
! 1602: void
! 1603: FDCDevice::CmdScanLowOrEqual()
! 1604: {
! 1605: CmdNotImplemented();
! 1606: }
! 1607:
! 1608: void
! 1609: FDCDevice::CmdScanHighOrEqual()
! 1610: {
! 1611: CmdNotImplemented();
! 1612: }
! 1613:
! 1614: void
! 1615: FDCDevice::CmdResetStandby()
! 1616: {
! 1617: CmdNotImplemented();
! 1618: }
! 1619:
! 1620: void
! 1621: FDCDevice::CmdSetStandby()
! 1622: {
! 1623: CmdNotImplemented();
! 1624: }
! 1625:
! 1626: void
! 1627: FDCDevice::CmdSoftwareReset()
! 1628: {
! 1629: ResetHard(false);
! 1630: }
! 1631:
! 1632: void
! 1633: FDCDevice::CmdInvalid()
! 1634: {
! 1635: CmdNotImplemented();
! 1636: }
! 1637:
! 1638: void
! 1639: FDCDevice::CmdNotImplemented()
! 1640: {
! 1641: UnitSelect(cmdbuf.data.hd_us);
! 1642: hd = (cmdbuf.data.hd_us & 0x04) >> 2;
! 1643:
! 1644: putlog(0, "Command %s NOT IMPLEMENTED", cmd->name);
! 1645: // とりあえずエラー
! 1646: st0.ic = ST0_IC_AT;
! 1647: st0.hd = hd;
! 1648: st0.us = us;
! 1649: ChangeInterrupt();
! 1650: CommandPhase();
! 1651: }
! 1652:
! 1653: // コマンド
! 1654: #define CMD(x) &FDCDevice::Cmd##x
! 1655: /*static*/ const std::vector<FDCDevice::FDCCmd>
! 1656: FDCDevice::cmd_list {
! 1657: // code mask cmdlen func name
! 1658: { 0x02, 0xbf, 9, CMD(ReadDiagnostic), "READ DIAGNOSTIC" },
! 1659: { 0x03, 0xff, 3, CMD(Specify), "SPECIFY" },
! 1660: { 0x04, 0xff, 2, CMD(SenseDeviceStatus), "SENSE DEVICE STATUS" },
! 1661: { 0x05, 0x3f, 9, CMD(WriteData), "WRITE DATA" },
! 1662: { 0x06, 0x1f, 9, CMD(ReadData), "READ DATA" },
! 1663: { 0x07, 0xff, 2, CMD(Recalibrate), "RECALIBRATE" },
! 1664: { 0x08, 0xff, 1, CMD(SenseInterruptStatus), "SENSE INTERRUPT STATUS" },
! 1665: { 0x09, 0x3f, 9, CMD(WriteDeletedData), "WRITE DELETED DATA" },
! 1666: { 0x0a, 0xbf, 2, CMD(ReadID), "READ ID" },
! 1667: { 0x0c, 0x1f, 9, CMD(ReadDeletedData), "READ DELETED DATA" },
! 1668: { 0x0d, 0xbf, 6, CMD(WriteID), "WRITE ID" },
! 1669: { 0x0f, 0xff, 3, CMD(Seek), "SEEK" },
! 1670: { 0x10, 0x1f, 1, CMD(Version), "VERSION" },
! 1671: { 0x11, 0x1f, 9, CMD(ScanEqual), "SCAN EQUAL" },
! 1672: { 0x19, 0x1f, 9, CMD(ScanLowOrEqual), "SCAN LOW OR EQUAL" },
! 1673: { 0x1d, 0x1f, 9, CMD(ScanHighOrEqual), "SCAN HIGH OR EQUAL" },
! 1674: { 0x34, 0xff, 1, CMD(ResetStandby), "RESET STANDBY" },
! 1675: { 0x35, 0xff, 1, CMD(SetStandby), "SET STANDBY" },
! 1676: { 0x36, 0xff, 1, CMD(SoftwareReset), "SOFTWARE RESET" },
! 1677:
! 1678: // リストの最後に Invalid を置く。
! 1679: // code=0x00, mask=0x00 なので必ず一致する。
! 1680: { 0x00, 0x00, 1, CMD(Invalid), "invalid" },
! 1681: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.