📄 cpmfs.hpp
字号:
// data from the message, identifies the target unit from the FCB // (placing a pointer to it in the XfReq), destroys the message and // calls the appropriate member of the array of the <CpmBdos> instances // with the XfReq pointer. The <CpmFs> takes no further part in the // operation. // 3. The <CpmBdos> instance formulates an appropriate disk transfer // (track, sector etc) inserting this into the <XfReq> object along // with a constant identifying which member function will deal with // the completed transfer. It then posts the <XfReq> to "its" disk // channel handler. // 4. The <XfReq> arrives at the channel handler and is placed in a FIFO // queue. When it finally gets actioned, a result code is placed in // the <XfReq> object and the <NxtFnc> member of the <CpmBdos> instance // called with the <XfReq*> as parameter via the object pointer. // 5. Using the constant placed in the XfReq by the previous member func, // CpmBdos::NxtFunc passes the XfReq to an internal, private function // which will do one off: // . Build a reply message and post it to the PID, then destroy the // XfReq object. This completes the cycle, un-blocking the Proc. // . Alter the XfReq params (eg when a directory block just read // needs to be re-written) and post to the channel handler again. // . Spawn another XfReq (as in the case of a read/write operation // that needs to re-access the directory to fetch the next extent), // "attaching" the XfReq pointer to the new one, so that operations // on it can continue after the intermediate operation has finished. // This allows infinite (!?) nesting of channel calls. // 6. A message arrives back at the originating process with the result // of the requested operation, effectively un-blocking it to continue // processing as dictated by internal logic. The FCB it passed in the // first place has been updated and data transfered to/from the buffer // set in the FCB. Simple, right? // class CpmBdos { friend class CpmFs; private: Knl *pTx; // copy of CpmFs Message dispatcher UINT16 nId; // copy of CpmFs unique identifier UINT16 nChan; // Channel ID for XfReq messages BYTE nFsc; // first physical sector number (0 or 1) BYTE nLsc; // last sector number BYTE nSkf; // optional sector skew factor UINT16 nBls; // data allocation block size UINT16 nDks; // number of blocks on disk UINT16 nDir; // number of directory entreies UINT16 nCks; // number of "checked" directory entries (for exchangable media) BYTE nOfs; // track offset of logical track 00 BYTE nAlb; // No of allocation blkd/extent (8 or 16) BYTE DirBuf[BLOCK_LEN]; // buffer for directory operations BYTE *pAlv; // Allocation Vector (dynamically allocated) BYTE *pCsv; // Checksum vector (for exchangable disks) BYTE *pXlt; // Sector skew table (NULL if no skew) DPB *pDpb; // physical characteristics for this unit void Open1 (XfReq*); // looping step in Open void Open2 (XfReq*, BOOL); // termination for Open void Close1 (XfReq*); // found file dir block void Close2 (XfReq*); // check update of dir blk void Creat1 (XfReq*); // end exist scan check void Creat2 (XfReq*); // looping step in Creat void Creat3 (XfReq*); // Success step in Creat void Stat1 (XfReq*); // looping step in Stat void Renam1 (XfReq*); // looping step in Rename void Renam2 (XfReq*); // success step in Rename void Remov1 (XfReq*); // looping step in Delete void Remov2 (XfReq*); // success step in Delete void Find1 (XfReq*); // first find success/fail void Find2 (XfReq*); // find next looping step void Read1 (XfReq*); // read complete step void Write1 (XfReq*); // write complete step void Write2 (XfReq*); // write open new extent step void Write3 (XfReq*); // write new extent intermediate void DoRetry(XfReq*); // disk error recovery void LogDisk (void); // build Allocation and Checksum vectors void LogDisk1 (XfReq*); // loop building Allocation vector UINT16 SecXlt (UINT16); // perform skew translation UINT16 GetFreeBlk (void); // locate first unused allocation block of unit void AllocBlk (UINT16, BYTE = 1); // set/reset numbered bit of disk allocation vector void ReGenerate (XfReq*, UINT16 = 0); // copy passed request and post the copy void DeAllocBlk (UINT16 n) { AllocBlk(n, 0); } void DirScanPrep (FCB*, UINT16*, UINT16*); void BlkDecode (UINT16, UINT16, UINT16*, UINT16*); protected: void Open (XfReq*); // locate by name if exists (wildcard ok) void Creat (XfReq*); // Create if name unique and entry available void Close (XfReq*); // Close file, update Dir. void Renam (XfReq*); // name change (not open!) void Remov (XfReq*); // remove file from system void Stat (XfReq*); // file size and protection void Find (XfReq*, UINT16); // directory name scanner void Read (XfReq*, UINT16); // Seq/Random read op. void Write (XfReq*, UINT16); // Seq/Random write op. CpmBdos (DPB&, DPARAM&, UINT16); // only friends can create public: ~CpmBdos(void); void NxtFunc (XfReq*); // the only public entry point }; ///////////////// // Disk transfer requests are held in a FIFO queue based on (guess what?) // We will use what Taligent term "implemenmtation inheritance", ie a // private base class. Thie code rests with the Disk driver stuff.. // class TreqstQ : private DblList { UINT16 nInq; public: TreqstQ (void) : nInq(0) { } ~TreqstQ (void) { } XfReq *Get (UINT16 = 0); XfReq *Peek (UINT16 = 0); void Purge (UINT16 = 0); void PurgeAll (void); UINT16 GetLen (void) { return nInq; } void Put (XfReq *p) { DblAppend((void*)p, sizeof(XfReq)); ++nInq; } }; //////////////// // for safety, we sub-class off the disk device transfer FIFO queue // preventing the animator form doing anthing bad with queue data.. // class TransQ { TreqstQ *pTQ; XfReq *pX; public: TransQ (TreqstQ *p) : pTQ(p) { } ~TransQ (void) { } void Peek (UINT16 n) { pX = pTQ->Peek(n); } UINT16 GetLen (void) { return pTQ->GetLen(); } UINT16 GetPid (void) { return pX->uProc; } UINT16 GetTrk (void) { return pX->nTrk; } UINT16 GetSec (void) { return pX->nSec; } UINT16 GetSid (void) { return pX->nSid; } BOOL IsRead (void) { return (pX->cmnd == DD_READ) ? TRUE : FALSE; } }; //////////// // This is the sequence of events in performing a disk transfer. Normally, // we must perform all 4, in sequence unless the heads already happen to // be on track, in which case we can go direct from IDEL to TRANSFER.. // enum ddState { DD_IDLE, DD_SEEK, DD_SETTLE, DD_WAIT, DD_TRANSFER }; ///////////////// // This class simulates a disk drive using preset constants, based on the // mS granularity system clock ticker, in terms of physical attributes of: // // * Track to track seek time // * Head settle time // * Rotational Latency // * Soft error rate // // Channel requests arrive via the usual message service. The model // actions these in FIFO, moving the "head" missile to the specified // "track", delaying for the head settle period, waiting until the // required "sector" "arrives" under the "head", then calling the Read/ // Write routine to do the transfer to the specific address. Finally, // the CpmBdos::NxtFunc member is called (again via the Object pointer // in the message), passing it the transfer request object pointer. A // parameter in the object indicates what routine is to precess the // completed transfer. If this is the end of the chain for the transfer, // the XfReq object is destroyed and the blocked process sent a message // to resume, otherwise the params are set for another transfer and the // message re-posted to join the FIFO queue again. // // This sounds convoluted, but it enables us to implement a BDOS which // is re-enterant, since all transfers are encapsulated totally in a // XfReq object which rattles around independant of, and inter-leaved // with, all other such objects without multi-threading or interrupts. // // One of these objects should be instantiated for each drive unit to be // modelled, but NOTE that it is important to use the same parameter // struct when initialising the corresponding File System object so that // it "knows" the size of its "drive". // class DskModel : public port { private: ddState stage; // stage of transfer (see enum type) BYTE nLid; // logical unit ID (0 = "A:" etc) BYTE nAngleInc; // rotate degrees per mS BYTE nTrkInc; // mS per head step BYTE nSettle; // mS to settle after step BYTE nSecAngle; // degrees per sector UINT16 nCurTrk; // current head location UINT16 nCurAngle; // current angle of sector index "hole" UINT32 nErrRate; // soft error rate (one every...) UINT32 nOpCnt; // count of operations for error emulation UINT32 lLastTime; // gated clock state memory UINT32 lHoldTime; // state memory for delayed events int fd; // handle for host "disk" data file BOOL bOk; // TRUE if disk is on-line and operational UINT16 nBps; // bytes per sector UINT32 nBpt; // bytes per cylinder UINT32 nBpd; // total bytes on "disk" TreqstQ ChanQ; // FIFO queue of requests for this unit BOOL Read (void); // read to buffer BLOCK_LEN bytes BOOL Write (void); // write from buffer BLOCK_LEN bytes long CalcOffset (void); // get offset for host file transfer public: DskModel (UINT16, UINT16, Knl*, char*, DPB&, BYTE); ~DskModel (void); void Scheduler (void); // called in "main loop" void RxPort (PMSG); // incomming message handler BOOL IsOnLine (void) { return bOk; } // check on c::tor success };#endif/////////////////////////////////// eof ////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -