📄 dskdrv.cpp
字号:
//*************************************************************************// MODULE : SimDsk - Conceptual Disk Driver for all types of drives. *// AUTHOR : Ron Chernich *// PURPOSE: This object Provides the Intermediate level IO routines *// (select, seek, etc) to interface between the File System and *// multiple instances of all types of physical disk units. *// HISTORY: *// 13-AUG-94 First version *// 17-AUG-94 Animation features added *//*************************************************************************#include "cpmfs.hpp" #include <fcntl.h>#ifdef _DOS_ENV #include <sys\types.h> #include <sys\stat.h> #include <io.h>#endif#ifdef UNIX #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>#endif#include <string.h>///////////////////////////////////////////////////////////////////////////// First we have the im plemenation of a FIFO queue that holds pending// disk transfer requests..//XfReq* TreqstQ::Get (UINT16 n){ XfReq *p = Peek(n); if (p) { DblDelete(); --nInq; } return p;}XfReq* TreqstQ::Peek (UINT16 n){ XfReq *p = (XfReq*)DblGetHead(); if (n) while (--n) if (NULL == (p = (XfReq*)DblGetNext())) break; return p;}void TreqstQ::Purge (UINT16 n){ XfReq *p = Get(n);}void TreqstQ::PurgeAll (void){ while (nInq) Purge();}///////////////////////////////////////////////////////////////////////////// The disk device is simulated through a host OS sequential file of the// passed mount name. If the file (created if necessary) length does not// match the "disk" parameters, it is "initialized" by writing all "0xE5"// records out to the required length. Info conveyed in the DPB are stored.//DskModel::DskModel (UINT16 id, UINT16 cls, Knl *pK, char *pSt, DPB& dsk, BYTE n) : port(id, cls, pK), nCurTrk(0), nCurAngle(0), nOpCnt(0), lHoldTime(0), nLid(n), bOk(FALSE), stage(DD_IDLE){ char stName[128], *cp; strcpy(stName, pSt); cp = strrchr(stName, '.'); if ((NULL == cp) || (strlen(stName) - (cp - stName) > 4)) strcat(stName, ".dsk"); #ifdef UNIX fd = open(stName, O_CREAT | O_RDWR, S_IREAD | S_IWRITE ); #endif #if defined(BC20) || defined(BC31) fd = open(stName, O_CREAT | O_BINARY | O_RDWR, S_IREAD | S_IWRITE); #endif #if !defined(BC31) && !defined(BC20) && !defined(UNIX) fd = open(stName, _O_CREAT | _O_BINARY | _O_RDWR, _S_IREAD | _S_IWRITE); #endif if (fd > 0) { struct stat info; message Msg(uID, ANI_DCREAT, nLid, (void*)&dsk.nTrks); stat(stName, &info); long nBlks = dsk.nSecs * dsk.nTrks * dsk.nSides; if (info.st_size != (long)(nBlks * dsk.nSecSize)) { char *pBuf = new char[dsk.nSecSize]; if (NULL == pBuf) return; memset(pBuf, 0xe5, dsk.nSecSize); for (int i = 0; i < nBlks; i++) if ((int)dsk.nSecSize != write(fd, pBuf, dsk.nSecSize)) return; //_commit(fd); DELETE_ARRAY pBuf; } nBps = dsk.nSecSize; nBpt = nBps * dsk.nSecs * dsk.nSides; nBpd = nBpt * dsk.nTrks; nSecAngle = (BYTE)(360 / (dsk.nSecs + 1)); nAngleInc = dsk.nAngleInc; nTrkInc = dsk.nTrkInc; nSettle = dsk.nSettle; nErrRate = dsk.nErrRate; lLastTime = Clock.GetTicks(); pTx->SendMsg(ID_ANIM, &Msg); bOk = TRUE; }}//////////////// Close our associated disk file.//DskModel::~DskModel (void){ if (fd > 0) close(fd);}/////////////// Calculate offset for a transfer, doing rudimentary validation// RETURNS -1 .. record out of range, else record number.//long DskModel::CalcOffset (void){ XfReq *p = ChanQ.Peek(); long nOfs = (p->nTrk * p->nSid * nBpt) + ((p->nSec - 1) * nBps); return (nOfs <= (long)(nBpd - nBps)) ? nOfs : -1L;}/////////////////////// Internal read simulates a "soft" (recoverable) error every <nErrRate>// operations. On others, it calcs the location of the sector sized// file sector to transfer and does so the address at the head of Q.// RETURNS: TRUE .. transfer Ok// FALSE .. bad params or soft (or even a host hard) error//BOOL DskModel::Read (void){ if (++nOpCnt >= nErrRate) nOpCnt = 0; else { XfReq *p = ChanQ.Peek(); long nOfs = CalcOffset(); if ((nOfs > 0L) && (nOfs == lseek(fd, nOfs, SEEK_SET)) && ((int)nBps == read(fd, p->pDma, nBps))) return TRUE; } return FALSE;}/////////////////////// Same sort of thing as for read operation..// RETURNS: TRUE .. transfer Ok// FALSE .. bad params or soft (or even a host hard) error//BOOL DskModel::Write (void){ if (++nOpCnt >= nErrRate) nOpCnt = 0; else { XfReq *p = ChanQ.Peek(); long nOfs = CalcOffset(); if ((nOfs > 0L) && (nOfs == lseek(fd, nOfs, SEEK_SET)) && ((int)nBps == write(fd, p->pDma, nBps))) return TRUE; } return FALSE;}///////////////// Port receive member simply adds passed Transfer Request to the queue// of transfer rwquests (FIFO). A message is sent to the animator in case// the disk model is being displayed, which will update the queue contents.//void DskModel::RxPort (PMSG pMsg){ if (pMsg->pBody) { ChanQ.Put((XfReq*)pMsg->pBody); TransQ Tq(&ChanQ); message msg(uID, ANI_CHAN_ENQ, nLid, &Tq); pTx->SendMsg(ID_ANIM, &msg); }}//////////////////// This member is called on every pass around the RCOS loop in <main>,// effectively acting as the disk sub-system's time slice. If there// are no transfers in the queue, we just update the angular disk// position and exit. If there is something at the head of queue, we://// 1. Step the heads until nCurTrk = required track (with animation),// 2. Delay for the head settle period,// 3. After the settle, calculate the delay until the disk rotation// (based on the current angular position) will bring the required// sector "under" the heads,// 4. Perform the read or write operation, then// 5. Remove the head of queue <XfReq> object, call the animator, then// call the CpmDsk::NxtFunc function (using the pointer in the XfReq)// which is analogous to issuing a "disk operation" complete interrupt.//void DskModel::Scheduler (void){ UINT32 lTm = Clock.GetTicks(); UINT16 nMs = (UINT16)(lTm - lLastTime); UINT16 ndTheta = nMs / nAngleInc; if (ndTheta) { if (361 < (nCurAngle += ndTheta)) nCurAngle %= 360; message msg(uID, ANI_SPIN, nLid, (void*)&nCurAngle); pTx->SendMsg(ID_ANIM, &msg); } lLastTime = lTm; // // If there is a transfer pending, do (in order): // 1. Seek to track // 2. Wait head settling time // 3. Wait out rotational latency // 4. Perform transfer. // if (by luck) we are already on track, skip steps 1 and 2 // if (ChanQ.GetLen()) { XfReq *p = ChanQ.Peek(); switch (stage) { // // The disk drive has nothing to do and a transfer is waiting. If we // are on-cylinder, we can immediatly begin scanning for the required // sector. Otherwise we'll have to go the whole hog - seek, settle etc.. // case DD_IDLE: if (nCurTrk != p->nTrk) { UINT16 nTargTrk = p->nTrk; if (nCurTrk < nTargTrk) nTargTrk |= 0x8000; message msg(uID, ANI_BEGIN_SEEK, nLid, (void*)&nTargTrk); pTx->SendMsg(ID_ANIM, &msg); stage = DD_SEEK; } else { message msg(uID, ANI_ONTRACK, p->nSec); pTx->SendMsg(ID_ANIM, &msg); UINT16 nSecPos = (p->nSec - 1) * nSecAngle; if (nSecPos < nCurAngle) lHoldTime = lLastTime + ((nCurAngle - nSecPos) / nAngleInc); else lHoldTime = lLastTime + ((360 - nCurAngle + nSecPos) / nAngleInc); stage = DD_TRANSFER; } break; // // We are seeking. Move the heads at the best rate the mechanics will // accomodate until we arrive on cylinder, then set in the anticipated // time after which the heads will be stable enough to start reading.. // case DD_SEEK: { INT16 nInc = nMs / nTrkInc; if (nInc) { INT16 nGap = abs(nCurTrk - p->nTrk); if (nInc > nGap) nInc = nGap; if (nCurTrk > p->nTrk) nInc = -nInc; message msg(uID, ANI_SEEK, 0,(void*)&nInc); pTx->SendMsg(ID_ANIM, &msg); nCurTrk += nInc; if (nCurTrk == p->nTrk) { lHoldTime = lLastTime; stage = DD_SETTLE; } } } break; // // We are on track and the head settling time has been set. If it has // expired, calculate rotational time delay until the desired sector // arrives.. // case DD_SETTLE: if (nSettle <= lLastTime - lHoldTime) { UINT16 nSecPos = (p->nSec - 1) * nSecAngle; if (nSecPos < nCurAngle) lHoldTime = lLastTime + ((nCurAngle - nSecPos) / nAngleInc); else lHoldTime = lLastTime + ((360 - nCurAngle + nSecPos) / nAngleInc); message msg(uID, ANI_ONTRACK, p->nSec); pTx->SendMsg(ID_ANIM, &msg); stage = DD_WAIT; } break; // // When we reach here, we see if the rotational delay has expired // case DD_WAIT: if (lLastTime >= lHoldTime) { lHoldTime = 0L; stage = DD_TRANSFER; } break; // // Ready to perform the read/write operation, de-queue the transfer // request, run the display animation, then callback (interrupt) the // file system to do the next stage of the BDOS operation.. // case DD_TRANSFER: { TransQ Tq(&ChanQ); message msg(uID, ANI_RWOP, nLid, &Tq); pTx->SendMsg(ID_ANIM, &msg); switch (p->cmnd) { case DD_READ : p->bRes = Read(); break; case DD_WRITE: p->bRes = Write(); break; } ChanQ.Purge(); stage = DD_IDLE; p->pFnCb->NxtFunc(p); } break; } }}/////////////////////////////////// eof ///////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -