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