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