|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// SCSI バス
//
#pragma once
#include "scsi.h"
#include <array>
class SCSIDevice;
class SCSIDisk;
class SCSIDomain;
class SCSIHostDevice;
class SCSITarget;
//
// SCSI バス
//
class SCSIBus : public Device
{
using inherited = Device;
public:
// (1<<id) が1つだけ立ててあるビットマップから ID を返す。
// ビットが立っていなければ -1 を返す? 複数立ってたら?
static inline int DecodeID(uint32 mask)
{
mask &= 0xff;
if (mask == 0)
return -1;
return 31 - __builtin_clz(mask);
}
public:
explicit SCSIBus(SCSIDomain *);
~SCSIBus() override;
bool Init() override;
void ResetHard(bool poweron) override;
SCSIDomain *GetDomain() const noexcept { return domain; }
// デバイス情報を更新する。
void Refresh();
// 信号線の操作
bool GetRST() const { return rst; }
void AssertRST();
void NegateRST();
bool GetREQ() const { return req; }
void AssertREQ();
void NegateREQ();
bool GetACK() const { return ack; }
void AssertACK();
void NegateACK();
bool GetATN() const { return atn; }
void AssertATN();
void NegateATN();
uint8 GetSEL() const { return sel; }
void AssertSEL(uint id);
void NegateSEL(uint id);
uint8 GetBSY() const { return bsy; }
void AssertBSY(uint id);
void NegateBSY(uint id);
bool GetMSG() const { return (xfer & SCSI::MSG); }
bool GetCD() const { return (xfer & SCSI::CD); }
bool GetIO() const { return (xfer & SCSI::IO); }
// MSG,CD,IO で示されるビットを val で指定した状態に変更する。
void SetXfer(uint8 val);
// 現在の情報転送フェーズ(の信号線の状態) を返す。
// 現在のフェーズが Transfer の時のみ戻り値の内容は有効。
SCSI::XferPhase GetXfer() const { return (SCSI::XferPhase)xfer; }
uint8 GetData() const { return data; }
void SetData(uint8 val) { data = val; }
// 現在のフェーズを返す
SCSI::Phase GetPhase() const { return phase; }
// フェーズを変更する
void SetPhase(SCSI::Phase val) { phase = val; }
// 現在のフェーズ名を返す
const char *GetPhaseName() const { return SCSI::GetPhaseName(phase); }
// 現在選択中のターゲットを返す、なければ NULL を返す。(モニタ用)
const SCSIDevice *GetSelectedTarget() const {
if (__predict_true(0 <= target_id && target_id < 8)) {
return device[target_id];
}
return NULL;
}
// 次の REQ アサートの前に指定のウェイトを入れる
// (シークタイムなどを簡易的に再現するため)。
// ウェイトは一度適用されると 0 に戻る。
void SetOneshotReqWait(uint64 tsec) { req_wait += tsec; }
private:
void BusFree(uint id);
void SelectionStart(Event *);
void Selected(Event *);
void SelectionAck(Event *);
void StartTransfer(Event *);
void TransferReq(Event *);
// 現在のフェーズ
SCSI::Phase phase {};
// SEL, BSY は同時に複数が重畳出来るので、アサートしたデバイスがそれぞれ
// 自身の ID を立てる。ネゲートは 0 かどうかで判断する。
// 現在の実装では実際には同時に立つことはないけど。
bool rst {};
bool req {};
bool ack {};
bool atn {};
uint8 sel {};
uint8 bsy {};
uint8 xfer {}; // MSG,CD,IO
uint8 data {};
// bool parity; 未実装
// 前回 ACK を立てた時刻。
// 転送速度をここで律速させている。
uint64 last_req_time {};
// 次の REQ アサートに入れるウェイト [tsec]
uint64 req_wait {};
// 実際のバスではそんなことはないが、ここではバスがイニシエータと
// ターゲットの ID を把握している。どちらも非選択状態なら -1。
int init_id {};
int target_id {};
// SCSI 管理
SCSIDomain *domain {};
// 接続されているデバイス (イニシエータも含む) へのポインタ。
std::array<SCSIDevice *, 8> device {};
// イベント
Event *event {};
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.