// $Id: Rw11CntlRK11.cpp 1186 2019-07-12 17:49:59Z mueller $
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright 2013-2019 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
// Other credits: 
//   the boot code is from the simh project and Copyright Robert M Supnik
// 
// Revision History: 
// Date         Rev Version  Comment
// 2019-04-19  1133   2.0.12 use ExecWibr()
// 2019-04-14  1131   2.0.11 proper unit init, call UnitSetupAll() in Start()
// 2019-02-23  1114   2.0.10 use std::bind instead of lambda
// 2018-12-19  1090   2.0.9  use RosPrintf(bool)
// 2018-12-15  1082   2.0.8  use std::bind or lambda instead of boost::bind
// 2018-12-09  1080   2.0.7  use HasVirt(); Virt() returns ref
// 2018-10-28  1062   2.0.6  replace boost/foreach
// 2017-04-02   865   2.0.5  Dump(): add detail arg
// 2017-03-03   858   2.0.4  use cntl name as message prefix
// 2017-02-26   857   2.0.3  use kCPAH_M_UBM22
// 2015-06-04   686   2.0.2  check for spurious lams
// 2015-02-17   647   2.0.1  use Nwrd2Nblk(); BUGFIX: revise RdmaPostExecCB()
// 2015-01-04   628   2.0    use Rw11RdmaDisk
// 2014-12-30   625   1.2    adopt to Rlink V4 attn logic
// 2014-12-25   621   1.1    adopt to 4k word ibus window
// 2014-06-14   562   1.0.1  Add stats
// 2013-04-20   508   1.0    Initial version
// 2013-02-10   485   0.1    First draft
// ---------------------------------------------------------------------------

/*!
  \brief   Implemenation of Rw11CntlRK11.
*/

#include <functional>

#include "librtools/RosFill.hpp"
#include "librtools/RosPrintBvi.hpp"
#include "librtools/RosPrintf.hpp"
#include "librtools/Rexception.hpp"
#include "librtools/RlogMsg.hpp"

#include "Rw11CntlRK11.hpp"
using namespace std::placeholders;

using namespace std;

/*!
  \class Retro::Rw11CntlRK11
  \brief FIXME_docs
*/

// all method definitions in namespace Retro
namespace Retro {

//------------------------------------------+-----------------------------------
// constants definitions

const uint16_t Rw11CntlRK11::kIbaddr;
const int      Rw11CntlRK11::kLam;

const uint16_t Rw11CntlRK11::kRKDS; 
const uint16_t Rw11CntlRK11::kRKER; 
const uint16_t Rw11CntlRK11::kRKCS; 
const uint16_t Rw11CntlRK11::kRKWC; 
const uint16_t Rw11CntlRK11::kRKBA; 
const uint16_t Rw11CntlRK11::kRKDA; 
const uint16_t Rw11CntlRK11::kRKMR; 

const uint16_t Rw11CntlRK11::kProbeOff;
const bool     Rw11CntlRK11::kProbeInt;
const bool     Rw11CntlRK11::kProbeRem;

const uint16_t Rw11CntlRK11::kRKDS_M_ID;
const uint16_t Rw11CntlRK11::kRKDS_V_ID;
const uint16_t Rw11CntlRK11::kRKDS_B_ID;
const uint16_t Rw11CntlRK11::kRKDS_M_HDEN;
const uint16_t Rw11CntlRK11::kRKDS_M_DRU;
const uint16_t Rw11CntlRK11::kRKDS_M_SIN;
const uint16_t Rw11CntlRK11::kRKDS_M_SOK;
const uint16_t Rw11CntlRK11::kRKDS_M_DRY;
const uint16_t Rw11CntlRK11::kRKDS_M_ADRY;
const uint16_t Rw11CntlRK11::kRKDS_M_WPS;
const uint16_t Rw11CntlRK11::kRKDS_B_SC;

const uint16_t Rw11CntlRK11::kRKER_M_DRE;
const uint16_t Rw11CntlRK11::kRKER_M_OVR;
const uint16_t Rw11CntlRK11::kRKER_M_WLO;
const uint16_t Rw11CntlRK11::kRKER_M_PGE;
const uint16_t Rw11CntlRK11::kRKER_M_NXM;
const uint16_t Rw11CntlRK11::kRKER_M_NXD;
const uint16_t Rw11CntlRK11::kRKER_M_NXC;
const uint16_t Rw11CntlRK11::kRKER_M_NXS;
const uint16_t Rw11CntlRK11::kRKER_M_CSE;
const uint16_t Rw11CntlRK11::kRKER_M_WCE;

const uint16_t Rw11CntlRK11::kRKCS_M_MAINT;
const uint16_t Rw11CntlRK11::kRKCS_M_IBA;
const uint16_t Rw11CntlRK11::kRKCS_M_FMT;
const uint16_t Rw11CntlRK11::kRKCS_M_RWA;
const uint16_t Rw11CntlRK11::kRKCS_M_SSE;
const uint16_t Rw11CntlRK11::kRKCS_M_RDY;
const uint16_t Rw11CntlRK11::kRKCS_M_MEX;
const uint16_t Rw11CntlRK11::kRKCS_V_MEX;
const uint16_t Rw11CntlRK11::kRKCS_B_MEX;
const uint16_t Rw11CntlRK11::kRKCS_V_FUNC;
const uint16_t Rw11CntlRK11::kRKCS_B_FUNC;
const uint16_t Rw11CntlRK11::kRKCS_M_GO;

const uint16_t Rw11CntlRK11::kFUNC_CRESET;
const uint16_t Rw11CntlRK11::kFUNC_WRITE;
const uint16_t Rw11CntlRK11::kFUNC_READ;
const uint16_t Rw11CntlRK11::kFUNC_WCHK;
const uint16_t Rw11CntlRK11::kFUNC_SEEK;
const uint16_t Rw11CntlRK11::kFUNC_RCHK;
const uint16_t Rw11CntlRK11::kFUNC_DRESET;
const uint16_t Rw11CntlRK11::kFUNC_WLOCK;

const uint16_t Rw11CntlRK11::kRKDA_M_DRSEL;
const uint16_t Rw11CntlRK11::kRKDA_V_DRSEL;
const uint16_t Rw11CntlRK11::kRKDA_B_DRSEL;
const uint16_t Rw11CntlRK11::kRKDA_M_CYL;
const uint16_t Rw11CntlRK11::kRKDA_V_CYL;
const uint16_t Rw11CntlRK11::kRKDA_B_CYL;
const uint16_t Rw11CntlRK11::kRKDA_M_SUR;
const uint16_t Rw11CntlRK11::kRKDA_V_SUR;
const uint16_t Rw11CntlRK11::kRKDA_B_SUR;
const uint16_t Rw11CntlRK11::kRKDA_B_SC;

const uint16_t Rw11CntlRK11::kRKMR_M_RID;
const uint16_t Rw11CntlRK11::kRKMR_V_RID;
const uint16_t Rw11CntlRK11::kRKMR_M_CRDONE;
const uint16_t Rw11CntlRK11::kRKMR_M_SBCLR;
const uint16_t Rw11CntlRK11::kRKMR_M_CRESET;
const uint16_t Rw11CntlRK11::kRKMR_M_FDONE;

//------------------------------------------+-----------------------------------
//! Default constructor

Rw11CntlRK11::Rw11CntlRK11()
  : Rw11CntlBase<Rw11UnitRK11,8>("rk11"),
    fPC_rkwc(0),
    fPC_rkba(0),
    fPC_rkda(0),
    fPC_rkmr(0),
    fPC_rkcs(0),
    fRd_rkcs(0),
    fRd_rkda(0),
    fRd_addr(0),
    fRd_lba(0),
    fRd_nwrd(0),
    fRd_fu(0),
    fRd_ovr(false),
    fRdma(this,
          std::bind(&Rw11CntlRK11::RdmaPreExecCB,  this, _1, _2, _3, _4),
          std::bind(&Rw11CntlRK11::RdmaPostExecCB, this, _1, _2, _3, _4))
{
  // must be here because Units have a back-ptr (not available at Rw11CntlBase)
  for (size_t i=0; i<NUnit(); i++) {
    fspUnit[i].reset(new Rw11UnitRK11(this, i));
  }

  fStats.Define(kStatNFuncCreset , "NFuncCreset"  , "func CRESET");
  fStats.Define(kStatNFuncWrite  , "NFuncWrite"   , "func WRITE");
  fStats.Define(kStatNFuncRead   , "NFuncRead"    , "func READ");
  fStats.Define(kStatNFuncWchk   , "NFuncWchk"    , "func WCHK");
  fStats.Define(kStatNFuncSeek   , "NFuncSeek"    , "func SEEK");
  fStats.Define(kStatNFuncRchk   , "NFuncRchk"    , "func RCHK");
  fStats.Define(kStatNFuncDreset , "NFuncDreset"  , "func DRESET");
  fStats.Define(kStatNFuncWlock  , "NFuncWlock "  , "func WLOCK");
}

//------------------------------------------+-----------------------------------
//! Destructor

Rw11CntlRK11::~Rw11CntlRK11()
{}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::Config(const std::string& name, uint16_t base, int lam)
{
  ConfigCntl(name, base, lam, kProbeOff, kProbeInt, kProbeRem);
  return;
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::Start()
{
  if (fStarted || fLam<0 || !fEnable || !fProbe.Found())
    throw Rexception("Rw11CntlRK11::Start",
                     "Bad state: started, no lam, not enable, not found");

  // add device register address ibus and rbus mappings
  // done here because now Cntl bound to Cpu and Cntl probed
  Cpu().AllIAddrMapInsert(Name()+".ds", Base() + kRKDS);
  Cpu().AllIAddrMapInsert(Name()+".er", Base() + kRKER);
  Cpu().AllIAddrMapInsert(Name()+".cs", Base() + kRKCS);
  Cpu().AllIAddrMapInsert(Name()+".wc", Base() + kRKWC);
  Cpu().AllIAddrMapInsert(Name()+".ba", Base() + kRKBA);
  Cpu().AllIAddrMapInsert(Name()+".da", Base() + kRKDA);
  Cpu().AllIAddrMapInsert(Name()+".mr", Base() + kRKMR);

  // ensure unit status is initialized
  UnitSetupAll();

  // setup primary info clist
  fPrimClist.Clear();
  fPrimClist.AddAttn();
  fPC_rkwc = Cpu().AddRibr(fPrimClist, fBase+kRKWC);
  fPC_rkba = Cpu().AddRibr(fPrimClist, fBase+kRKBA);
  fPC_rkda = Cpu().AddRibr(fPrimClist, fBase+kRKDA);
  fPC_rkmr = Cpu().AddRibr(fPrimClist, fBase+kRKMR); // read to monitor CRDONE
  fPC_rkcs = Cpu().AddRibr(fPrimClist, fBase+kRKCS);

  // add attn handler
  Server().AddAttnHandler(bind(&Rw11CntlRK11::AttnHandler, this, _1), 
                          uint16_t(1)<<fLam, this);

  fStarted = true;
  return;
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::UnitSetup(size_t ind)
{
  Rw11UnitRK11& unit = *fspUnit[ind];

  uint16_t rkds = ind<<kRKDS_V_ID;
  if (unit.HasVirt()) {                     // file attached
    rkds |= kRKDS_M_HDEN;                   // always high density
    rkds |= kRKDS_M_SOK;                    // always sector counter OK ?FIXME?
    rkds |= kRKDS_M_DRY;                    // drive available
    rkds |= kRKDS_M_ADRY;                   // access available
    if (unit.WProt())                       // in case write protected
      rkds |= kRKDS_M_WPS;
  }
  unit.SetRkds(rkds);
  Cpu().ExecWibr(fBase+kRKDS, rkds);

  return;
}  

//------------------------------------------+-----------------------------------
//! FIXME_docs

bool Rw11CntlRK11::BootCode(size_t unit, std::vector<uint16_t>& code, 
                            uint16_t& aload, uint16_t& astart)
{
  uint16_t kBOOT_START = 02000;
  uint16_t bootcode[] = {      // rk11 boot loader - from simh pdp11_rk.c (v3.9)
    0042113,                   // "KD"
    0012706, kBOOT_START,      // MOV #boot_start, SP
    0012700, uint16_t(unit),   // MOV #unit, R0        ; unit number
    0010003,                   // MOV R0, R3
    0000303,                   // SWAB R3
    0006303,                   // ASL R3
    0006303,                   // ASL R3
    0006303,                   // ASL R3
    0006303,                   // ASL R3
    0006303,                   // ASL R3
    0012701, 0177412,          // MOV #RKDA, R1        ; rkda
    0010311,                   // MOV R3, (R1)         ; load da
    0005041,                   // CLR -(R1)            ; clear ba
    0012741, 0177000,          // MOV #-256.*2, -(R1)  ; load wc
    0012741, 0000005,          // MOV #READ+GO, -(R1)  ; read & go
    0005002,                   // CLR R2
    0005003,                   // CLR R3
    0012704, uint16_t(kBOOT_START+020),  // MOV #START+20, R4 ; ?? unclear ??
    0005005,                   // CLR R5
    0105711,                   // TSTB (R1)
    0100376,                   // BPL .-4
    0105011,                   // CLRB (R1)
    0005007                    // CLR PC     (5007)
  };
  
  code.clear();
  code.insert(code.end(), std::begin(bootcode), std::end(bootcode));
  aload  = kBOOT_START;
  astart = kBOOT_START+2;
  return true;
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::Dump(std::ostream& os, int ind, const char* text,
                        int detail) const
{
  RosFill bl(ind);
  os << bl << (text?text:"--") << "Rw11CntlRK11 @ " << this << endl;
  os << bl << "  fPC_rkwc:        " << fPC_rkwc << endl;
  os << bl << "  fPC_rkba:        " << fPC_rkba << endl;
  os << bl << "  fPC_rkda:        " << fPC_rkda << endl;
  os << bl << "  fPC_rkmr:        " << fPC_rkmr << endl;
  os << bl << "  fPC_rkcs:        " << fPC_rkcs << endl;
  os << bl << "  fRd_rkcs:        " << fRd_rkcs << endl;
  os << bl << "  fRd_rkda:        " << fRd_rkda << endl;
  os << bl << "  fRd_addr:        " << fRd_addr << endl;
  os << bl << "  fRd_lba:         " << fRd_lba  << endl;
  os << bl << "  fRd_nwrd:        " << fRd_nwrd << endl;
  os << bl << "  fRd_fu:          " << fRd_fu   << endl;
  os << bl << "  fRd_ovr:         " << RosPrintf(fRd_ovr)  << endl;
  fRdma.Dump(os, ind+2, "fRdma: ", detail);
  Rw11CntlBase<Rw11UnitRK11,8>::Dump(os, ind, " ^", detail);
  return;
}
  
//------------------------------------------+-----------------------------------
//! FIXME_docs

int Rw11CntlRK11::AttnHandler(RlinkServer::AttnArgs& args)
{
  fStats.Inc(kStatNAttnHdl);
  Server().GetAttnInfo(args, fPrimClist);

  uint16_t rkwc = fPrimClist[fPC_rkwc].Data();
  uint16_t rkba = fPrimClist[fPC_rkba].Data();
  uint16_t rkda = fPrimClist[fPC_rkda].Data();
  //uint16_t rkmr = fPrimClist[fPC_rkmr].Data();
  uint16_t rkcs = fPrimClist[fPC_rkcs].Data();

  uint16_t se   =  rkda                 & kRKDA_B_SC;
  uint16_t hd   = (rkda>>kRKDA_V_SUR)   & kRKDA_B_SUR;
  uint16_t cy   = (rkda>>kRKDA_V_CYL)   & kRKDA_B_CYL;
  uint16_t dr   = (rkda>>kRKDA_V_DRSEL) & kRKDA_B_DRSEL;
 
  bool go       =  rkcs & kRKCS_M_GO;
  uint16_t fu   = (rkcs>>kRKCS_V_FUNC)  & kRKCS_B_FUNC;
  uint16_t mex  = (rkcs>>kRKCS_V_MEX)   & kRKCS_B_MEX;
  uint32_t addr = uint32_t(mex)<<16 | uint32_t(rkba);

  // Note: apparently are operands first promoted to 32 bit -> mask after ~ !
  uint32_t nwrd = (~uint32_t(rkwc)&0xffff) + 1; // transfer size in words

  if (!go) {
    RlogMsg lmsg(LogFile());
    lmsg << "-I " << Name() << ":"
         << " cs=" << RosPrintBvi(rkcs,8)
         << "  go=0, spurious attn, dropped";
    return 0;
  }
  
  // all 8 units are always available, but check anyway
  if (dr > NUnit())
    throw Rexception("Rw11CntlRK11::AttnHandler","Bad state: dr > NUnit()");

  Rw11UnitRK11& unit = *fspUnit[dr];
  Rw11Cpu& cpu = Cpu();
  RlinkCommandList clist;

  uint32_t lba  = unit.Chs2Lba(cy,hd,se);
  uint32_t nblk = unit.Nwrd2Nblk(nwrd);

  uint16_t rker = 0;
  uint16_t rkds = unit.Rkds();

  if (fTraceLevel>0) {
    RlogMsg lmsg(LogFile());
    static const char* fumnemo[8] = {"cr","w ","r ","wc","sk","rc","dr","wl"};
    
    lmsg << "-I " << Name() << ":"
         << " cs=" << RosPrintBvi(rkcs,8)
         << " da=" << RosPrintBvi(rkda,8)
         << " ad=" << RosPrintBvi(addr,8,18)
         << " fu=" << fumnemo[fu&0x7]
         << " pa=" << dr 
         << "," << RosPrintf(cy,"d",3) 
         << "," << hd 
         << "," << RosPrintf(se,"d",2)
         << " la,nw=" << RosPrintf(lba,"d",4) 
         << "," << RosPrintf(nwrd,"d",5);
  }

  // check for spurious interrupts (either RDY=1 or RDY=0 and rdma busy)
  if ((rkcs & kRKCS_M_RDY) || fRdma.IsActive()) {
    RlogMsg lmsg(LogFile());
    lmsg << "-E " << Name() << ":   err"
         << "  cr=" << RosPrintBvi(rkcs,8)
         << " spurious lam: "
         << (fRdma.IsActive() ? "RDY=0 and Rdma busy" : "RDY=1");
    return 0;
  }

  // check for general abort conditions
  if (fu != kFUNC_CRESET &&                 // function not control reset
      (!unit.HasVirt())) {                  //   and drive not attached
    rker = kRKER_M_NXD;                     //   --> abort with NXD error

  } else if (fu != kFUNC_WRITE &&           // function neither write
             fu != kFUNC_READ &&            //   nor read
             (rkcs & (kRKCS_M_FMT|kRKCS_M_RWA))) { // and FMT or RWA set 
    rker = kRKER_M_PGE;                     //   --> abort with PGE error
  } else if (rkcs & kRKCS_M_RWA) {          // RWA not supported
    rker = kRKER_M_DRE;                     //   --> abort with DRE error
  }
  
  if (rker) {
    cpu.AddWibr(clist, fBase+kRKER, rker);
    if (fu == kFUNC_SEEK || fu == kFUNC_DRESET) 
      cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_SBCLR | (1u<<dr));
    cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_FDONE);
    LogRker(rker);
    Server().Exec(clist);
    return 0;
  }

  // check for overrun (read/write beyond cylinder 203)
  // if found, truncate request length
  bool ovr = lba + nblk > unit.NBlock();
  if (ovr) nwrd = (unit.NBlock()-lba) * (unit.BlockSize()/2);

  // remember request parameters for call back
  fRd_rkcs  = rkcs;
  fRd_rkda  = rkda;
  fRd_addr  = addr;
  fRd_lba   = lba;
  fRd_nwrd  = nwrd;
  fRd_ovr   = ovr;
  fRd_fu    = fu;

  // now handle the functions
  if (fu == kFUNC_CRESET) {                 // Control reset -----------------
    fStats.Inc(kStatNFuncCreset);
    cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_CRESET);

  } else if (fu == kFUNC_WRITE) {           // Write -------------------------
                                            //   Note: WRITE+FMT is just WRITE
    fStats.Inc(kStatNFuncWrite);
    if (se >= unit.NSector())   rker |= kRKER_M_NXS;
    if (cy >= unit.NCylinder()) rker |= kRKER_M_NXC;
    if (unit.WProt())           rker |= kRKER_M_WLO;
    if (rkcs & kRKCS_M_IBA) rker |= kRKER_M_DRE;  // IBA not supported
    if (rker) {
      AddErrorExit(clist, rker);
    } else {
      fRdma.QueueDiskWrite(addr, nwrd, Rw11Cpu::kCPAH_M_UBM22, lba, &unit);
    }

  } else if (fu == kFUNC_READ) {            // Read --------------------------
    fStats.Inc(kStatNFuncRead);
    if (se >= unit.NSector())   rker |= kRKER_M_NXS;
    if (cy >= unit.NCylinder()) rker |= kRKER_M_NXC;
    if (rkcs & kRKCS_M_IBA) rker |= kRKER_M_DRE;  // IBA not supported
    if (rker) {
      AddErrorExit(clist, rker);
    } else {
      fRdma.QueueDiskRead(addr, nwrd, Rw11Cpu::kCPAH_M_UBM22, lba, &unit);
    }

  } else if (fu == kFUNC_WCHK) {            // Write Check -------------------
    fStats.Inc(kStatNFuncWchk);
    if (se >= unit.NSector())   rker |= kRKER_M_NXS;
    if (cy >= unit.NCylinder()) rker |= kRKER_M_NXC;
    if (rkcs & kRKCS_M_IBA) rker |= kRKER_M_DRE;  // IBA not supported
    if (rker) {
      AddErrorExit(clist, rker);
    } else {
      fRdma.QueueDiskWriteCheck(addr, nwrd, Rw11Cpu::kCPAH_M_UBM22, lba, &unit);
    }

  } else if (fu == kFUNC_SEEK) {            // Seek --------------------------
    fStats.Inc(kStatNFuncSeek);
    if (se >= unit.NSector())   rker |= kRKER_M_NXS;
    if (cy >= unit.NCylinder()) rker |= kRKER_M_NXC;
    if (rker) {
      cpu.AddWibr(clist, fBase+kRKER, rker);
      cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_SBCLR | (1u<<dr));
      cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_FDONE);
      LogRker(rker);
    } else {
      cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_FDONE);
      rkds &= ~kRKDS_B_SC;                  // replace current sector number
      rkds |= se;
      unit.SetRkds(rkds);
      cpu.AddWibr(clist, fBase+kRKDS, rkds);
      cpu.AddWibr(clist, fBase+kRKMR, 1u<<dr); // issue seek done
    }

  } else if (fu == kFUNC_RCHK) {            // Read Check --------------------
    fStats.Inc(kStatNFuncRchk);
    if (se >= unit.NSector())   rker |= kRKER_M_NXS;
    if (cy >= unit.NCylinder()) rker |= kRKER_M_NXC;
    if (rkcs & kRKCS_M_IBA) rker |= kRKER_M_DRE;  // IBA not supported
    if (rker) {
      AddErrorExit(clist, rker);
    } else {
      AddNormalExit(clist, nwrd, 0);        // no action, virt disks don't err
    }
    
  } else if (fu == kFUNC_DRESET) {          // Drive Reset -------------------
    fStats.Inc(kStatNFuncDreset);
    cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_FDONE);
    cpu.AddWibr(clist, fBase+kRKMR, 1u<<dr);   // issue seek done
    
  } else if (fu == kFUNC_WLOCK) {           // Write Lock --------------------
    fStats.Inc(kStatNFuncWlock);
    rkds |= kRKDS_M_WPS;                    // set RKDS write protect flag
    unit.SetRkds(rkds);
    unit.SetWProt(true);
    cpu.AddWibr(clist, fBase+kRKDS, rkds);
    cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_FDONE);
  }

  if (clist.Size()) {                       // if handled directly
    Server().Exec(clist);                   // doit
  }
  return 0;
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::RdmaPreExecCB(int stat, size_t nwdone, size_t nwnext,
                                 RlinkCommandList& clist)
{
  // if last chunk and not doing WCHK add a labo and normal exit csr update
  if (stat == Rw11Rdma::kStatusBusyLast && fRd_fu != kFUNC_WCHK) {
    clist.AddLabo();
    AddNormalExit(clist, nwdone+nwnext, 0);
  }
  return;
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::RdmaPostExecCB(int stat, size_t ndone,
                                  RlinkCommandList& clist, size_t ncmd)
{
  if (stat == Rw11Rdma::kStatusBusy) return;

  uint16_t rker = 0;

  // handle write check
  if (fRd_fu == kFUNC_WCHK) {
    size_t nwcok = fRdma.WriteCheck(ndone);
    if (nwcok != ndone) {                   // if mismatch found
      rker |= kRKER_M_WCE;                  // set error flag
      if (fRd_rkcs & kRKCS_M_SSE) {         // if 'stop-on-soft' requested
        ndone = nwcok;                      // truncate word count
      }
    }
  }
  
  // handle Rdma aborts
  if (stat == Rw11Rdma::kStatusFailRdma) rker |= kRKER_M_NXM;

  // check for fused csr updates
  if (clist.Size() > ncmd) {
    uint8_t  ccode = clist[ncmd].Command();
    uint16_t cdata = clist[ncmd].Data();
    if (ccode != RlinkCommand::kCmdLabo || (rker != 0 && cdata == 0))
      throw Rexception("Rw11CntlRK11::RdmaPostExecCB",
                       "Bad state: Labo not found or missed abort");
    if (cdata == 0) return;
  }

  // finally to RK11 register update
  RlinkCommandList clist1;
  AddNormalExit(clist1, ndone, rker);
  Server().Exec(clist1);

  return;
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::LogRker(uint16_t rker)
{
  RlogMsg lmsg(LogFile());
  lmsg << "-E " << Name() << ": er=" << RosPrintBvi(rker,8) << "  ERROR ABORT";
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::AddErrorExit(RlinkCommandList& clist, uint16_t rker)
{
  Rw11Cpu& cpu = Cpu();
  cpu.AddWibr(clist, fBase+kRKER, rker);
  cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_FDONE);
  LogRker(rker);
  return;
}

//------------------------------------------+-----------------------------------
//! FIXME_docs

void Rw11CntlRK11::AddNormalExit(RlinkCommandList& clist, size_t ndone,
                                 uint16_t rker)
{
  Rw11Cpu& cpu  = Cpu();
  uint16_t dr   = (fRd_rkda>>kRKDA_V_DRSEL) & kRKDA_B_DRSEL;
  Rw11UnitRK11& unit = *fspUnit[dr];

  size_t nblk   = unit.Nwrd2Nblk(ndone);

  uint32_t addr = fRd_addr + 2*ndone;
  size_t   lba  = fRd_lba  + nblk;
  uint32_t nrest = fRd_nwrd - ndone;

  uint16_t ba   = addr & 0177776;           // get lower 16 bits
  uint16_t mex  = (addr>>16) & 03;          // get upper  2 bits
  uint16_t cs   = (fRd_rkcs & ~kRKCS_M_MEX) | (mex << kRKCS_V_MEX);
  uint16_t se;
  uint16_t hd;
  uint16_t cy;
  unit.Lba2Chs(lba, cy,hd,se);
  uint16_t da   = (fRd_rkda & kRKDA_M_DRSEL) | (cy<<kRKDA_V_CYL) |
                  (hd<<kRKDA_V_SUR) | se;

  if (fRd_ovr) rker |= kRKER_M_OVR;

  if (rker) {
    cpu.AddWibr(clist, fBase+kRKER, rker);
    LogRker(rker);
  }
  cpu.AddWibr(clist, fBase+kRKWC, uint16_t((-nrest)&0177777));
  cpu.AddWibr(clist, fBase+kRKBA, ba);
  cpu.AddWibr(clist, fBase+kRKDA, da);
  if (cs != fRd_rkcs) 
    cpu.AddWibr(clist, fBase+kRKCS, cs);
  cpu.AddWibr(clist, fBase+kRKMR, kRKMR_M_FDONE);

  return;
}


} // end namespace Retro
