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