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