--- nono/vm/fdc.cpp 2026/04/29 17:05:14 1.1.1.10 +++ nono/vm/fdc.cpp 2026/04/29 17:05:38 1.1.1.15 @@ -75,53 +75,41 @@ // OP.SEL.x でドライブごとに有効になる信号 // EJECT, EJECT MASK, LED BLINK, DISK IN, ERR DISK +// ドライブセレクトイネーブルがネゲートされていて、 +// どのドライブも選択されていない状態になったとき、 +// 各入力信号はプルアップされているため、 +// 負論理の信号はすべてネゲート状態となる。 + #include "fdc.h" #include "bitops.h" #include "config.h" #include "dmac.h" #include "fdd.h" -#include "mpu.h" +#include "monitor.h" #include "pedec.h" #include "scheduler.h" -// グローバル参照用 -FDCDevice *gFDC; - // time [nsec] 後に呼び出す phase_event のコールバックをセットする。 // func の書式が面倒なのを省略して書きたいため。 #define CallAfter(func_, time_) do { \ phase_event.func = ToEventCallback(&FDCDevice::func_); \ phase_event.time = time_; \ - gScheduler->RestartEvent(phase_event); \ + scheduler->RestartEvent(phase_event); \ } while (0) // コンストラクタ FDCDevice::FDCDevice() - : inherited("FDC") + : inherited(OBJ_FDC) { - devaddr = baseaddr; - devlen = 0x2000; - - phase_event.Regist("FDC Phase"); - - poll_event.func = ToEventCallback(&FDCDevice::PollEventCallback); - poll_event.time = 1024_usec; - poll_event.Regist("FDC Polling"); - - motor_event.func = ToEventCallback(&FDCDevice::MotorEventCallback); - motor_event.time = 38.54_sec; - motor_event.Regist("FDC SED9420A TMOUT"); - - monitor.func = ToMonitorCallback(&FDCDevice::MonitorUpdate); + monitor = gMonitorManager->Regist(ID_MONITOR_FDC, this); + monitor->func = ToMonitorCallback(&FDCDevice::MonitorUpdate); // サイズは Create() 時に確定する - monitor.Regist(ID_MONITOR_FDC); } // デストラクタ FDCDevice::~FDCDevice() { - gFDC = NULL; } bool @@ -140,7 +128,7 @@ FDCDevice::Create() } // モニタサイズはここで決定。FDD は横に2台で、それ以上は縦に伸ばす。 - monitor.SetSize(80, 15 + (ndrive / 2) * 10); + monitor->SetSize(80, 15 + (ndrive / 2) * 10); // vector は面倒なので一旦 unique_ptr[] に確保して // 生ポインタの vector を作る。うーん。 @@ -158,64 +146,107 @@ FDCDevice::Create() return true; } +// 初期化 +bool +FDCDevice::Init() +{ + dmac = GetDMACDevice(); + pedec = GetPEDECDevice(); + + phase_event.SetName("FDC Phase"); + scheduler->RegistEvent(phase_event); + + poll_event.func = ToEventCallback(&FDCDevice::PollEventCallback); + poll_event.time = 1024_usec; + poll_event.SetName("FDC Polling"); + scheduler->RegistEvent(poll_event); + + motor_event.func = ToEventCallback(&FDCDevice::MotorEventCallback); + motor_event.time = 38.54_sec; + motor_event.SetName("FDC SED9420A TMOUT"); + scheduler->RegistEvent(motor_event); + + return true; +} + // リセット void FDCDevice::ResetHard(bool poweron) { + if (poweron) { + // READY ポーリングはリセットの影響を受けないはず + initialized = false; + scheduler->StopEvent(motor_event); + } + // uPD765 マニュアルによると、リセット信号によるリセットでは // SRT, HUT, HLT は影響を受けない。 // シリンダ位置 pcn はとりあえずわからないので放置。 - if (poweron) { - // READY ポーリングはリセットの影響を受けないはず - initialized = false; - gScheduler->StopEvent(motor_event); - } + // US0,US1 を low (FDD0) に。 + us = 0; + // 割り込み (と DRQ) をネゲート。 + msr.ndm = false; + st0.ic = ST0_IC_IC; + st0.se = false; + st0.ec = false; + st0.nr = false; + st0.hd = false; + st0.us = 0; + ChangeInterrupt(); - gScheduler->StopEvent(phase_event); - gScheduler->StopEvent(poll_event); + scheduler->StopEvent(phase_event); + scheduler->StopEvent(poll_event); CommandPhase(); } -uint64 -FDCDevice::Read(uint32 offset) +busdata +FDCDevice::ReadPort(uint32 offset) { - gMPU->AddCycle(19); // InsideOut p.135 + busdata data; switch (offset) { case 0: - { if (dack) { // DACK が立っているときは A0 によらずデータレジスタをアクセス - return ReadData(); + data = ReadData(); + } else { + data = GetMSR(); + putlog(4, "STAT -> $%02x", data.Data()); } - - uint8 stat = GetMSR(); - putlog(4, "STAT -> $%02x", stat); - return stat; - } + break; case 1: - return ReadData(); + data = ReadData(); + break; case 2: - return ReadDriveStatus(); + data = ReadDriveStatus(); + break; case 3: - return 0xff; + data = 0xff; + break; + + default: + VMPANIC("corrupted offset=%u", offset); } - __unreachable(); + + // InsideOut p.135 + const busdata wait = busdata::Wait(19 * 40_nsec); + data |= wait; + + data |= BusData::Size1; + return data; } -uint64 -FDCDevice::Write(uint32 offset, uint32 data) +busdata +FDCDevice::WritePort(uint32 offset, uint32 data) { - gMPU->AddCycle(19); // InsideOut p.135 - switch (offset) { case 0: if (dack) { // DACK が立っているときは A0 によらずデータレジスタをアクセス WriteDataReg(data); - return 0; + break; } // ここはフェーズに関わらず 0x34, 0x35, 0x36 のコマンドだけを @@ -236,29 +267,31 @@ FDCDevice::Write(uint32 offset, uint32 d data); break; } - return 0; + break; case 1: WriteCmd(data); - return 0; + break; case 2: { // ドライブコントロールレジスタ putlog(1, "Drive Control <- $%02x", data); - // FDD 割り込みクリア(要実機確認) - gPEDEC->NegateINT(fdd_vector[0]); - uint8 bit = 0x01; for (const auto f : fdd_vector) { + // ドライブセレクタのビットが %1 -> %0 になると動作する。 if (f && bit_falling(drivectrl, data, bit)) { f->SetDriveCtrl(data & 0xe0); + + // FDD 割り込みのクリアは詳細不明だが、とりあえず + // 同じタイミングで動作するようにしておく。 + pedec->NegateINT(f); } bit <<= 1; } drivectrl = data; - return 0; + break; } case 3: @@ -268,14 +301,14 @@ FDCDevice::Write(uint32 offset, uint32 d bool motor_on_bit_new = data & 0x80; if (motor_on_bit_new) { - putlog(1, "Access Drive <- $%02x (MotorOn %s #%d)", data, + putlog(1, "Access Drive <- $%02x (MotorOn %s #%u)", data, is2DD ? "2DD" : "2HD", accdrive); if (motor_on_bit == false) { // MOTOR_ON ビットが立ち上がったら、全ドライブに通知 // motor_event は止める。 - gScheduler->StopEvent(motor_event); + scheduler->StopEvent(motor_event); for (const auto f : fdd_vector) { if (f) { f->SetMotorOn(true); @@ -294,12 +327,12 @@ FDCDevice::Write(uint32 offset, uint32 d if (motor_on_bit) { // MOTOR_ON ビットが立ち下がったら、一定時間後に MOTOR_ON // 信号をネゲートする。 - gScheduler->RestartEvent(motor_event); + scheduler->RestartEvent(motor_event); } } // FDD 割り込みクリア(要実機確認) - gPEDEC->NegateINT(fdd_vector[0]); + pedec->NegateINT(fdd_vector[0]); motor_on_bit = motor_on_bit_new; @@ -310,14 +343,22 @@ FDCDevice::Write(uint32 offset, uint32 d f->SetDriveSelect(accdrive, is2DD); } } - return 0; + break; } + default: + VMPANIC("corrupted offset=%u", offset); } - __unreachable(); + + // InsideOut p.135 + const busdata wait = busdata::Wait(19 * 40_nsec); + + busdata r = wait; + r |= BusData::Size1; + return r; } -uint64 -FDCDevice::Peek(uint32 offset) +busdata +FDCDevice::PeekPort(uint32 offset) { switch (offset) { case 0: @@ -343,9 +384,8 @@ FDCDevice::Peek(uint32 offset) return resbuf.Peek(); default: - __unreachable(); + return 0xff; } - break; } case 2: @@ -355,10 +395,16 @@ FDCDevice::Peek(uint32 offset) return 0xff; default: - __unreachable(); + return 0xff; } } +bool +FDCDevice::PokePort(uint32 offset, uint32 data) +{ + return false; +} + // モニタ更新 void FDCDevice::MonitorUpdate(Monitor *, TextScreen& screen) @@ -444,17 +490,17 @@ FDCDevice::MonitorUpdate(Monitor *, Text x = 52; y = 0; - screen.Print(x, y++, "HD=%d", hd); - screen.Print(x, y++, "US=%d", us); - screen.Print(x, y++, "TC=%d", tc); - screen.Print(x, y++, "DACK=%d", dack); + screen.Print(x, y++, "HD=%u", hd); + screen.Print(x, y++, "US=%u", us); + screen.Print(x, y++, "TC=%u", tc); + screen.Print(x, y++, "DACK=%u", dack); x = 60; y = 0; - screen.Print(x, y++, "srt=%d (seek=%d)", srt, seek_srt); - screen.Print(x, y++, "hut=%d", hut); - screen.Print(x, y++, "hlt=%d", hlt); - screen.Print(x, y++, "nd=%d", nd); + screen.Print(x, y++, "srt=%u (seek=%u)", srt, seek_srt); + screen.Print(x, y++, "hut=%u", hut); + screen.Print(x, y++, "hlt=%u", hlt); + screen.Print(x, y++, "nd=%u", nd); x = 52; y = 5; @@ -472,7 +518,7 @@ FDCDevice::MonitorUpdate(Monitor *, Text screen.Print(x, y++, "Phase: %s", phase_str[(int)phase]); if (cmdbuf.len > 0) { screen.Puts(x, y, "Command:"); - for (int i = 0; i < cmdbuf.len; i++) { + for (uint i = 0; i < cmdbuf.len; i++) { screen.Print(x + 9 + i * 4, y, "$%02x", cmdbuf.buf[i]); } screen.Print(x + 49, y, "(%s)", cmd->name); @@ -493,9 +539,9 @@ FDCDevice::MonitorUpdate(Monitor *, Text f = NULL; } - screen.Print(x, y, "#%d", i); + screen.Print(x, y, "#%u", i); if (f) { - screen.Print(x + 5, y, "%3d %3d", u.pcn, u.ncn); + screen.Print(x + 5, y, "%3d %3u", u.pcn, u.ncn); screen.Print(x + 13, y, TA::OnOff(u.ready), "Ready"); static const char * state_str[] = { "Idle", @@ -528,7 +574,7 @@ void FDCDevice::MonitorReg(TextScreen& screen, int x, int y, uint8 reg, const char * const *names) { - for (int i = 0; i < 8; i++) { + for (uint i = 0; i < 8; i++) { if (names[i][0] == '-') { screen.Puts(x + i * 4, y, TA::Disable, "---"); } else { @@ -688,9 +734,8 @@ FDCDevice::ReadData() return data; default: - VMPANIC("corrupted phase=$%x", (int)phase); + VMPANIC("corrupted phase=$%x", (uint)phase); } - __unreachable(); } // ドライブステータスレジスタ取得 @@ -736,7 +781,7 @@ FDCDevice::WriteCmd(uint32 data) } msr.rqm = false; - putlog(2, "CMD[%d] <- $%02x", cmdbuf.len, data); + putlog(2, "CMD[%u] <- $%02x", cmdbuf.len, data); cmdbuf.buf[cmdbuf.len++] = data; // コマンドの1バイト目でディスパッチ if (cmdbuf.len == 1) { @@ -765,7 +810,7 @@ FDCDevice::WriteCmd(uint32 data) break; default: - VMPANIC("corrupted phase=$%x", (int)phase); + VMPANIC("corrupted phase=$%x", (uint)phase); } } @@ -838,10 +883,10 @@ FDCDevice::ChangeInterrupt() { if (GetST0() != 0x80 || (msr.ndm && 0/* XXX DRQ*/)) { putlog(4, "AssertINT"); - gPEDEC->AssertINT(this); + pedec->AssertINT(this); } else { putlog(4, "NegateINT"); - gPEDEC->NegateINT(this); + pedec->NegateINT(this); } } @@ -861,7 +906,7 @@ FDCDevice::ReadyChanged(int unit, bool i } // 転送の同期を取るために、READY になった時刻を覚えておく if (is_ready) { - unitinfo[unit].ready_time = gScheduler->GetVirtTime(); + unitinfo[unit].ready_time = scheduler->GetVirtTime(); } } @@ -871,7 +916,7 @@ FDCDevice::StartPoll() { if (poll_event.IsRunning() == false) { putlog(3, "Start polling"); - gScheduler->StartEvent(poll_event); + scheduler->StartEvent(poll_event); } } @@ -903,7 +948,7 @@ FDCDevice::PollEventCallback(Event& ev) } if (is_poll) { - gScheduler->RestartEvent(ev); + scheduler->RestartEvent(ev); } else { putlog(3, "Stop polling"); } @@ -1012,7 +1057,7 @@ FDCDevice::AssertDACK(bool tc_) dack = true; tc = tc_; // DRQ を下げる - gDMAC->NegateREQ(0); + dmac->NegateREQ(0); } // DACK 信号をネゲートする (DMAC から呼ばれる) @@ -1039,8 +1084,8 @@ FDCDevice::PushDreg(uint8 data) } // 内部データレジスタから data を取り出す。 -// 空なら -1 を返す。 -int +// 空なら (uint32)-1 を返す。 +uint32 FDCDevice::PopDreg() { uint8 data; @@ -1053,7 +1098,7 @@ FDCDevice::PopDreg() st1.overrun = true; st0.ic = ST0_IC_AT; ChangeInterrupt(); - return -1; + return (uint32)-1; } } @@ -1064,7 +1109,7 @@ FDCDevice::RequestDataByte() if (nd) { putlog(0, "CPU Transfer mode (NOT IMPLEMENTED)"); } else { - gDMAC->AssertREQ(0); + dmac->AssertREQ(0); } } @@ -1077,7 +1122,7 @@ FDCDevice::UnitSelect(uint8 us_) // LUNA では XP 側の PSG の I/O と US 信号線の組み合わせで決まる。 us = us_ & 0x03; - putlog(3, "UnitSelect US:%d", us); + putlog(3, "UnitSelect US:%u", us); // いまのところ LUNA のことは忘れる } @@ -1093,6 +1138,12 @@ FDCDevice::DriveSelect(uint8 drive) fdd = NULL; } + // ドライブセレクト信号線が変わったら READY が変わるかもしれないので + // ポーリングを再開する必要がある。 + if (initialized) { + StartPoll(); + } + // いまのところ LUNA のことは忘れる } @@ -1124,7 +1175,7 @@ FDCDevice::CmdSpecify() hut = cmdbuf.specify.srt_hut & 15; hlt = cmdbuf.specify.hlt_nd >> 1; nd = cmdbuf.specify.hlt_nd & 1; - putlog(2, " SRT:%d HUT:%d HLT:%d ND:%d", srt, hut, hlt, nd); + putlog(2, " SRT:%u HUT:%u HLT:%u ND:%u", srt, hut, hlt, nd); // 初期化完了、ポーリング開始 initialized = true; @@ -1172,13 +1223,13 @@ FDCDevice::CmdWriteData() hd = (cmdbuf.data.hd_us & 0x04) >> 2; if (loglevel >= 2) { - putlogn(" MT:%d MF:%d SK:%d HD:%d", (int)mt, (int)mf, (int)sk, hd); - putlogn(" C:%2d H:%d R:%2d N:%d", + putlogn(" MT:%u MF:%u SK:%u HD:%u", (uint)mt, (uint)mf, (uint)sk, hd); + putlogn(" C:%2u H:%u R:%2u N:%u", cmdbuf.data.c, cmdbuf.data.h, cmdbuf.data.r, cmdbuf.data.n); - putlogn(" EOT:%2d GSL:$%02x DTL:$%02x", + putlogn(" EOT:%2u GSL:$%02x DTL:$%02x", cmdbuf.data.eot, cmdbuf.data.gsl, cmdbuf.data.dtl); @@ -1199,13 +1250,13 @@ FDCDevice::CmdReadData() hd = (cmdbuf.data.hd_us & 0x04) >> 2; if (loglevel >= 2) { - putlogn(" MT:%d MF:%d SK:%d HD:%d", (int)mt, (int)mf, (int)sk, hd); - putlogn(" C:%2d H:%d R:%2d N:%d", + putlogn(" MT:%u MF:%u SK:%u HD:%u", (uint)mt, (uint)mf, (uint)sk, hd); + putlogn(" C:%2u H:%u R:%2u N:%u", cmdbuf.data.c, cmdbuf.data.h, cmdbuf.data.r, cmdbuf.data.n); - putlogn(" EOT:%2d GSL:$%02x DTL:$%02x", + putlogn(" EOT:%2u GSL:$%02x DTL:$%02x", cmdbuf.data.eot, cmdbuf.data.gsl, cmdbuf.data.dtl); @@ -1242,7 +1293,7 @@ FDCDevice::XferStart(bool is_write) // FDD がバイトを読み書きするちょうど真ん中のタイミングで動くように // 同期してみる。 - uint64 now = gScheduler->GetVirtTime(); + uint64 now = scheduler->GetVirtTime(); uint64 t = 16_usec - ((now - unitinfo[us].ready_time) % 16_usec); t += 8_usec; @@ -1280,7 +1331,7 @@ FDCDevice::XferStartCallback(Event& ev) resbuf.data.h = fdd->PeekByte(2); resbuf.data.r = fdd->PeekByte(3); resbuf.data.n = fdd->PeekByte(4); - putlog(2, "IDAM C:%2d H:%d R:%2d N:%d", + putlog(2, "IDAM C:%2u H:%u R:%2u N:%u", resbuf.data.c, resbuf.data.h, resbuf.data.r, @@ -1351,7 +1402,7 @@ FDCDevice::XferStartCallback(Event& ev) // 次のマークまで待つ ev.time = dist * 16_usec; } - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); } void @@ -1365,8 +1416,8 @@ FDCDevice::XferDataCallback(Event& ev) if (xfer_remain > 0) { if (__predict_false(xfer_write)) { - int data = PopDreg(); - if (data >= 0) { + uint32 data = PopDreg(); + if ((int32)data >= 0) { fdd->WriteByte(data); } else { // XXX: こうじゃなくてエラービットだけ立ててセクタ終了時処理かも @@ -1449,7 +1500,7 @@ FDCDevice::XferDataCallback(Event& ev) if (xfer_write && xfer_remain > 0) { RequestDataByte(); } - gScheduler->StartEvent(ev); + scheduler->StartEvent(ev); } void @@ -1544,7 +1595,7 @@ FDCDevice::CmdReadID() mf = cmdbuf.code & 0x40; hd = (cmdbuf.data.hd_us & 0x04) >> 2; - putlog(2, " MF:%d HD:%d", (int)mf, hd); + putlog(2, " MF:%u HD:%u", (uint)mf, hd); XferStart(false); } @@ -1582,7 +1633,7 @@ FDCDevice::CmdSeek() msr.db[us] = true; unitinfo[us].ncn = cmdbuf.seek.ncn; unitinfo[us].state = UnitState::Seek; - putlog(2, " NCN:%d", unitinfo[us].ncn); + putlog(2, " NCN:%u", unitinfo[us].ncn); CommandPhase(); StartPoll(); }