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