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