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