|
|
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: //
! 8: // FDC (uPD72065B)
! 9: //
! 10:
1.1 root 11: #include "fdc.h"
1.1.1.9 ! root 12: #include "mpu.h"
1.1.1.6 root 13: #include "pedec.h"
1.1 root 14: #include "scheduler.h"
15:
1.1.1.9 ! root 16: // グローバル参照用
! 17: FDCDevice *gFDC;
1.1.1.6 root 18:
1.1 root 19: // コンストラクタ
20: FDCDevice::FDCDevice()
1.1.1.8 root 21: : inherited("FDC")
1.1 root 22: {
23: devaddr = baseaddr;
24: devlen = 0x2000;
25:
1.1.1.9 ! root 26: event.func = ToEventCallback(&FDCDevice::Callback);
! 27: event.Regist("FDC");
! 28:
! 29: // XXX ハック。ちゃんと作ること
! 30: fdd[0].ready = true;
! 31: fdd[1].ready = true;
! 32: fdd[2].ready = false;
! 33: fdd[3].ready = false;
1.1 root 34: }
35:
36: // デストラクタ
37: FDCDevice::~FDCDevice()
38: {
1.1.1.9 ! root 39: gFDC = NULL;
1.1 root 40: }
41:
1.1.1.9 ! root 42: // リセット
1.1 root 43: void
1.1.1.9 ! root 44: FDCDevice::ResetHard(bool poweron)
1.1 root 45: {
46: // XXX 他には
1.1.1.9 ! root 47: gScheduler->StopEvent(event);
1.1 root 48: idle_phase();
49: }
50:
51: uint64
1.1.1.7 root 52: FDCDevice::Read(uint32 offset)
1.1 root 53: {
1.1.1.5 root 54: gMPU->AddCycle(19); // InsideOut p.135
55:
1.1.1.7 root 56: switch (offset) {
1.1.1.4 root 57: case 0:
1.1 root 58: return ReadStatus();
1.1.1.4 root 59: case 1:
1.1 root 60: return ReadData();
1.1.1.4 root 61: case 2:
1.1 root 62: return ReadDriveStatus();
1.1.1.4 root 63: case 3:
64: return 0xff;
1.1 root 65: }
1.1.1.4 root 66: __unreachable();
1.1 root 67: }
68:
69: uint64
1.1.1.7 root 70: FDCDevice::Write(uint32 offset, uint32 data)
1.1 root 71: {
1.1.1.5 root 72: gMPU->AddCycle(19); // InsideOut p.135
73:
1.1.1.7 root 74: switch (offset) {
1.1.1.4 root 75: case 0:
1.1.1.9 ! root 76: putlog(0, "未実装コマンドレジスタ書き込み $%02x", data);
1.1.1.4 root 77: return 0;
1.1 root 78:
1.1.1.4 root 79: case 1:
1.1 root 80: WriteCmd(data);
81: return 0;
1.1.1.4 root 82:
83: case 2:
1.1 root 84: if ((data & 0x0f) != 0) {
85: fdc.drivesel = data & 0x0f;
86: putlog(1, "ドライブ選択%s%s%s%s",
87: (fdc.drivesel & 1) ? " #0" : "",
88: (fdc.drivesel & 2) ? " #1" : "",
89: (fdc.drivesel & 4) ? " #2" : "",
90: (fdc.drivesel & 8) ? " #3" : "");
91: }
92: if ((data & 0xe0) != 0) {
1.1.1.7 root 93: putlog(0, "未実装ビット書き込み $%06x <- $%02x",
94: gMPU->GetPaddr(), data);
1.1 root 95: }
96: return 0;
97:
1.1.1.4 root 98: case 3:
1.1 root 99: fdc.is2DD = ((data & 0x10) != 0);
100: fdc.accdrive = data & 3;
101: putlog(1, "アクセスドライブ %s #%d",
102: fdc.is2DD ? "2DD" : "2HD",
103: fdc.accdrive);
104: fdd[fdc.accdrive].motor = (data & 0x80);
105: if (fdd[fdc.accdrive].motor) {
106: putlog(1, "ドライブ #%d モーターオン", fdc.accdrive);
107: } else {
108: putlog(1, "ドライブ #%d モーターオフ", fdc.accdrive);
109: }
110: return 0;
111: }
1.1.1.4 root 112: __unreachable();
1.1 root 113: }
114:
115: uint64
1.1.1.7 root 116: FDCDevice::Peek(uint32 offset)
1.1 root 117: {
1.1.1.4 root 118: // XXX 未実装
1.1 root 119: return 0xff;
120: }
121:
122: // FDC ステータスレジスタ読み込み
123: uint32
124: FDCDevice::ReadStatus()
125: {
126: uint8 stat = 0;
127:
128: if (fdc.RQM) {
129: stat |= FDC::STAT_RQM;
130: }
131: if (fdc.DIO == DIOtoHost) {
132: stat |= FDC::STAT_DIO;
133: }
134: if (fdc.NDM) {
135: stat |= FDC::STAT_NDM;
136: }
137: if (fdc.CB) {
138: stat |= FDC::STAT_CB;
139: }
140: for (int i = 0; i < 4; i++) {
141: if (fdd[i].seek) {
142: stat |= (1 << i);
143: }
144: }
145:
146: putlog(2, "STAT -> $%02x", stat);
147: return stat;
148: }
149:
150: // FDC データレジスタ読み込み
151: uint32
152: FDCDevice::ReadData()
153: {
154: uint8 data;
155:
156: data = 0xff;
157: if (fdc.respos < fdc.reslen) {
158: data = fdc.resbuf[fdc.respos++];
159: }
160: putlog(2, "DATA -> $%02x", data);
161:
162: if (fdc.respos == fdc.reslen) {
163: idle_phase();
164: }
165: return data;
166: }
167:
168: // ドライブステータスレジスタ読み込み
169: uint32
170: FDCDevice::ReadDriveStatus()
171: {
172: uint8 data;
173:
174: // ディスク非挿入しかサポートしていない
175: data = 0x00;
176: putlog(1, "ドライブステータスレジスタ読み込み; ディスク非挿入");
177: return data;
178: }
179:
180: // FDC コマンドレジスタ書き込み
181: void
182: FDCDevice::WriteCmd(uint32 data)
183: {
184: // cmdpos のインクリメントはログ表示の後で行なっている
185: fdc.cmdbuf[fdc.cmdpos] = data;
186:
187: // 1バイト目はコマンドで、それによって長さが変わる。
188: // コマンド名をログに表示したい。
189: if (fdc.cmdpos == 0) {
190: // コマンドはまず下位5ビットであたりをつける
191: switch (data & FDC::CMD_MASK) {
192: case FDC::CMD_SPECIFY:
193: fdc.cmdlen = 3;
194: break;
195: case FDC::CMD_SENSE_DEVICE_STATUS:
196: fdc.cmdlen = 2;
197: break;
198: case FDC::CMD_SENSE_INTERRUPT_STATUS:
199: fdc.cmdlen = 1;
200: break;
201: case FDC::CMD_RECALIBRATE:
202: fdc.cmdlen = 2;
203: break;
204: case FDC::CMD_SEEK:
205: fdc.cmdlen = 3;
206: break;
207:
208: default:
209: // 未実装コマンド
210: fdc.cmdlen = 1;
211: }
212:
213: const char *cmdname = cmd2name(data & FDC::CMD_MASK);
214: putlog(2, "CMD[%d] <- $%02x(%s)", fdc.cmdpos, data, cmdname);
215: } else {
216: putlog(2, "CMD[%d] <- $%02x", fdc.cmdpos, data);
217: // CB は2バイト目から
218: fdc.CB = true;
219: }
220: // ここでインクリメント
221: fdc.cmdpos++;
222:
223: // 全部受け取ったらフェーズ遷移
224: if (fdc.cmdpos >= fdc.cmdlen) {
225: exec_phase();
226: }
227: }
228:
229: // idle フェーズに移行
230: void
231: FDCDevice::idle_phase()
232: {
233: fdc.phase = PHASE_IDLE;
234: fdc.RQM = true;
235: fdc.DIO = DIOtoFDC;
236: fdc.NDM = false;
237: fdc.CB = false;
238: fdc.cmdpos = 0;
239: fdc.cmdlen = 0;
240: fdc.respos = 0;
241: fdc.reslen = 0;
242: putlog(2, "Idle フェーズ");
243: }
244:
245: // Exec フェーズに移行
246: void
247: FDCDevice::exec_phase()
248: {
249: fdc.phase = PHASE_EXEC;
250: fdc.DIO = DIOtoFDC;
251: fdc.CB = true;
252: if (fdc.nd) {
253: fdc.NDM = true; // XXX ?
254: }
255:
256: switch (fdc.cmdbuf[0] & FDC::CMD_MASK) {
257: case FDC::CMD_SPECIFY:
258: putlog(1, "Exec フェーズ: SPECIFY");
259: fdc.srt = fdc.cmdbuf[1] >> 4;
260: fdc.hut = fdc.cmdbuf[1] & 15;
261: fdc.hlt = fdc.cmdbuf[2] >> 1;
262: fdc.nd = (fdc.cmdbuf[2] & 1);
263: idle_phase();
264: break;
265:
266: case FDC::CMD_SENSE_DEVICE_STATUS:
267: putlog(1, "Exec フェーズ: SENSE DEVICE STATUS");
1.1.1.9 ! root 268: uint8 st3;
! 269: st3 = fdc.cmdbuf[1] & 0x07;
! 270: if ((force_ready || fdd[fdc.accdrive].ready) == true) {
! 271: st3 |= FDC::ST3_RY;
! 272: }
! 273: fdc.resbuf[0] = st3;
1.1 root 274: fdc.reslen = 1;
275: result_phase();
276: break;
277:
278: case FDC::CMD_SENSE_INTERRUPT_STATUS:
279: {
280: putlog(1, "Exec フェーズ: SENSE INTERRUPT STATUS");
1.1.1.6 root 281: // XXX とりあえずここで割り込みを下げる。
282: // 実際には割り込み状態をラッチして信号線に出すというコマンドで
283: // 割り込み要因がオフになっていればそれによってネゲートされる
284: // ということのようだ。
1.1.1.9 ! root 285: if (interrupt == false) {
! 286: // XXX 割り込みが起きていないときは INVALID と同じらしい
! 287: fdc.resbuf[0] = 0x80;
! 288: fdc.reslen = 1;
! 289: result_phase();
! 290: break;
! 291: }
1.1.1.6 root 292: gPEDEC->NegateINT(this);
1.1.1.9 ! root 293: interrupt = false;
1.1 root 294: // XXX まだフロッピー挿入はない
295: uint8 st0 = 0;
296: st0 |= FDC::ST0_IC_NT;
1.1.1.9 ! root 297: if ((force_ready || fdd[fdc.accdrive].ready) == false) {
! 298: st0 |= FDC::ST0_NR;
! 299: }
1.1 root 300: st0 |= fdc.accdrive;
301: fdc.resbuf[0] = st0;
302: fdc.resbuf[1] = 0; // XXX PCN
303: fdc.reslen = 2;
304: result_phase();
305: break;
306: }
307:
308: case FDC::CMD_RECALIBRATE:
309: putlog(1, "Exec フェーズ: RECALIBRATE");
310: fdd[fdc.accdrive].seek = true;
311: // SEEK/RECALIBRATE では CB はセットされない
312: fdc.CB = false;
313: idle_phase();
314: // XXX ヘッドの位置という概念がまだない
315: event.time = 0;
1.1.1.9 ! root 316: event.code = FDC::CMD_RECALIBRATE; // XXX コマンドをメンバ変数にするとか
! 317: gScheduler->RestartEvent(event);
1.1 root 318: break;
319:
320: case FDC::CMD_SEEK:
321: putlog(1, "Exec フェーズ: SEEK (未実装)");
322: // SEEK/RECALIBRATE では CB はセットされない
323: fdc.CB = false;
324: idle_phase();
325: break;
326:
327: default:
328: putlog(0, "未実装 Exec フェーズ $%02x", fdc.cmdbuf[0]);
329: break;
330: }
331: }
332:
333: // Result フェーズに移行
334: void
335: FDCDevice::result_phase()
336: {
337: putlog(1, "Result フェーズ");
338: fdc.phase = PHASE_RESULT;
339: fdc.RQM = true;
340: fdc.DIO = DIOtoHost;
341: fdc.NDM = false;
342: fdc.CB = true;
343: }
344:
345: // イベントコールバック
346: void
1.1.1.5 root 347: FDCDevice::Callback(Event& ev)
1.1 root 348: {
1.1.1.5 root 349: switch (ev.code) {
1.1 root 350: case FDC::CMD_RECALIBRATE:
351: fdd[fdc.accdrive].seek = false;
1.1.1.9 ! root 352: fdc.DIO = DIOtoFDC;
1.1 root 353: Interrupt();
354: break;
355: }
356: }
357:
358: // 割り込みを上げる
359: void
360: FDCDevice::Interrupt()
361: {
1.1.1.6 root 362: gPEDEC->AssertINT(this);
1.1.1.9 ! root 363: interrupt = true;
! 364: }
! 365:
! 366: // 強制レディ信号の設定
! 367: void
! 368: FDCDevice::SetForceReady(bool force_ready_)
! 369: {
! 370: force_ready = force_ready_;
1.1 root 371: }
372:
373: // コマンド名
374: /* static */ const char *
375: FDCDevice::cmdnames_str[] = {
376: NULL, // 0x00
377: NULL, // 0x01
378: "READ DIAGNOSTIC", // 0x02
379: "SPECIFY", // 0x03
380: "SENSE DEVICE STATUS", // 0x04
381: "WRITE DATA", // 0x05
382: "READ", // 0x06
383: "RECALIBRATE", // 0x07
384: "SENSE INTERRUPT STATUS", // 0x08
385: "WRITE DELETED DATA", // 0x09
386: "READ ID", // 0x0a
387: NULL, // 0x0b
388: "READ DELETED DATA", // 0x0c
389: "WRITE ID", // 0x0d
390: NULL, // 0x0e
391: "SEEK", // 0x0f
392: NULL, // 0x10
393: "SCAN EQUAL", // 0x11
394: NULL, // 0x12
395: NULL, // 0x13
396: "RESET STANDBY", // 0x14
397: "SET STANDBY", // 0x15
398: "SOFTWARE RESET", // 0x16
399: NULL, // 0x17
400: NULL, // 0x18
401: "SCAN LOW OR EQUAL", // 0x19
402: NULL, // 0x1a
403: NULL, // 0x1b
404: NULL, // 0x1c
405: "SCAN HIGH OR EQUAL", // 0x1d
406: };
407:
408: // コマンドを名前に変換
409: /* static */ const char *
410: FDCDevice::cmd2name(uint32 cmd)
411: {
412: if (cmd < countof(cmdnames_str) && cmdnames_str[cmd]) {
413: return cmdnames_str[cmd];
414: } else {
415: return "?";
416: }
417: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.