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