⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpmbdos.cpp

📁 一个嵌入式系统的C代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//*************************************************************************//  MODULE : CpmBdos - The Basic Disk Operating System object members     *//  AUTHOR : Ron Chernich                                                 *//  PURPOSE: This is the actual CP/M-like BDOS (Basic Disk Operating      *//           System), providing file operations and low level block       *//           access. Normally, char at a time buffering would be provided *//           by the compiler - but RCOS builds this into the Exec/PCI.    *//  HISTORY:                                                              *//    13-AUG-94 First version                                             *//*************************************************************************#include "cpmfs.hpp"                        ////////////////////////////////////////////////////////////////////////////         The BDOS .. Instantiate one of these per disk drive.//------------------------------------------------------------------------// Each file consists of 0..31 "extents" where an extent is a physical// entry in the directory.  Each extent can contain either 8 or 16// disk allocation blocks, depending on the total blocks on the media.// This is because 16 bytes of each dir entry are reserved for allocation// block numbers.  IBM 3740 floppies have 243 blocks total, hence a block// ID fits in one byte.  Bigger disks need two bytes per block number.//   (Aside: MS-DOS compromised, using 12 bits for a group number)// A block is a power of two multiple of 1024 bytes - this is the smallest// piece of disk that can be allocated.  When (say) 16 blocks have been// allocated, a new "extent" is opened, duplicating the file name in the// first free directory slot.  Blocks are allocated on a first free basis// from the "Allocation Vector" for the disk.//// CP/M supported only block at a time transfers (it was up to compilers to// implement byte at a time access).  Entry <nRecCnt> contains the "current"// 128 byte record of the last allocation block that is in use. If you write// beyond the last 128 byte record of the last block, a new block is // allocated, opening extents as required.  Finding out where you are in// the current rec of the last block required an EOF mark (usually 0x1a).// When introduced, MS-DOS kept an actual byte count, making the EOF mark// redundant.  But, to this day, some old ported proggies still write 0x1a// EOF marks in text files!//// For an IBM 3740 8" diskette with 241 available 1K blocks, each extent// can reference 16K and the maximum 32 extents for one file references// more than the disk can hold.  Larger disks required larger block sizes.////////////////////////////////////////////////////////////////////////////// Class constructor sets params for the unit, allocates space for the// checksum vector (if we have any checked Dir entries), creates the// allocation vector (which will be initialised when the disk is "logged")// If the sector skew factor is not zero, a translate table is built.//CpmBdos::CpmBdos (DPB& dpb, DPARAM& mac, UINT16 nChanId) :  pDpb(&dpb), nFsc(mac.nFsc), nLsc(mac.nLsc), nSkf(mac.nSkf),  nBls(mac.nBls), nDks(mac.nDks), nDir(mac.nDir), nCks(mac.nCks),  nOfs(mac.nOfs), nChan(nChanId){  nAlb = (nDks > 255) ? 8 : 16;  if (0 == nCks)    pCsv = NULL;  else {    pCsv = new BYTE[nCks];    if (NULL == pCsv)      return;    memset(pCsv, 0xff, nCks);  }  if (0 == nSkf)    pXlt = NULL;  else {    int nSecs = nLsc + (nFsc ? 0 : 1);    if (NULL == (pXlt = new BYTE[nSecs]))      return;    int nSec = nFsc;    for (int i = 0; i < nSecs; i++) {      *(pXlt + i) = nSec;      nSec += nSkf;      if (nSec > nLsc) {        nSec -= nLsc;        if (nSec == nFsc)          ++nSec;      }    }  }  pAlv = new BYTE[(nDks + 7) / 8];}////////////////// shut down//CpmBdos::~CpmBdos (void){  if (pAlv)    DELETE_ARRAY pAlv;  if (pXlt)    DELETE_ARRAY pXlt;  if (pCsv)    DELETE_ARRAY pCsv;}/////////////////////// The public interface used to simulate interrupt service vectoring.// The disk controller channel device calls her when a disk operation// has finished, supplying the packet of info (a XfReq object) that// encapsulates the transfer. The member who launched the packet knew// where to go next and placed an appropriate constant in the packet,// so at the mere expense of a few (!) nested stack frames..//void CpmBdos::NxtFunc (XfReq *pX){  switch (pX->ivec) {    case LOG1   : LogDisk1(pX); break;    case CLOSE1 : Close1(pX);   break;    case CLOSE2 : Close2(pX);   break;    case OPEN1  : Open1(pX);    break;    case CREAT1 : Creat1(pX);   break;    case CREAT2 : Creat2(pX);   break;    case CREAT3 : Creat3(pX);   break;    case READ0  : Read(pX, 0);  break;    case READ1  : Read1(pX);    break;    case WRITE1 : Write1(pX);   break;    case REMOV1 : Remov1(pX);   break;    case REMOV2 : Remov2(pX);   break;    case WRITE2 : break; }}///////////////////////////////////////////////////////////////////////////                      These Members are PRIVATE//----------------------------------------------------------------------// The BDOS re-enterent mechanism runs by re-posting messages to itself.// Since the Kernel::Run message dispatcher destroys messages after// they have been dispatched, we must copy the message to be re-posted// otherwise we end up multiply free-ing mamory (which is very bad).//void CpmBdos::ReGenerate (XfReq* pOld, UINT16 nTag){  XfReq *pNew = new XfReq;  *pNew = *pOld;  message msg(nId, 0, nTag, (void*)pNew);  pTx->PostMsg(nChan, &msg);}//////////////// Set/reset the passed bit of the disk allocation vector//void CpmBdos::AllocBlk (UINT16 nBit, BYTE nState){  UINT16 nByte = nBit / 8;  BYTE   nMask = 0x80 >> (nBit % 8);  if (nState)    *(pAlv + nByte) |= nMask;  else    *(pAlv + nByte) &= ~nMask;}//////////// Scan the Disk Alocation vector for the first free block// number (ie first zero bit, working left to right)// RETURNS: block number, or zero if disk full!//UINT16 CpmBdos::GetFreeBlk (void){  UINT16 nByte = 0;  BYTE nMask = 0x80;    while (nByte < nBls) {    for (int i = 0; i < 8; i++)      if (0x00 == (*(pAlv + nByte) & nMask >> i))        return (UINT16)((nByte * 8) + i);    ++nByte;  }  return 0x00;}///////////// Set sector for transfer -// using sector skew translate table if not null//UINT16 CpmBdos::SecXlt (UINT16 nLogSec){  return (pXlt) ? *(pXlt + nLogSec - nFsc) : (nLogSec);}/////////////// Calculate track and sector numbers of passed record within block..//void CpmBdos::BlkDecode (UINT16 nBlk, UINT16 nRec,                         UINT16 *pnTrk, UINT16 *pnSec){  if (nBlk < nDks) {    UINT16 nSecs = (nBlk * nBls / pDpb->nSecSize) + nRec;    *pnTrk = nOfs + (UINT16)(nSecs / pDpb->nSecs);    *pnSec = SecXlt(nFsc + (UINT16)(nSecs % pDpb->nSecs));  }}/////////////// A read/write has failed. Decrement the retry counter and if not zero,// post to the channel controller to try it again. If the error persists,// see your doctor (ghod, i hope this never gets called to the end).//void CpmBdos::DoRetry (XfReq *pX){  if (--(pX->nRetry)) {    message msg(nId, 16, 16, (void*)pX);    pTx->PostMsg(nChan, &msg);  }  else {    if (pX->pNxt) {      XfReq *pInner = pX->pNxt;      pInner->bRes = FALSE;      delete pX;      NxtFunc(pInner);      return;    }    if (LOG1 == pX->ivec)      delete pX->pFcb;    else {      message msg(nId, FS_Ok);      pTx->PostMsg(pX->uProc, &msg);    }  }}/////////////////// Several (lots) of members need to prepare for a full directory scan,// so this little 'fella sets up the passed FCB for the common stuff..//void CpmBdos::DirScanPrep (FCB *pFcb, UINT16 *pnTrk, UINT16 *pnSec){  pFcb->de.nRes1 = 0;  pFcb->de.nRes2 = 0;  pFcb->de.nExtent = 0;  pFcb->nCurRec = nDir / 4;  BlkDecode(0, 0, pnTrk, pnSec);}///////////////////// Begin the process of Logging the Disk by creating an XfReq for the// first record of the directory and queueing it for execution. Function// <LogDisk1> will receive the service "interrupt" and issue more// requests until logging is complete.  Here I make an improvement on// CP/M - A blank disk is all 0xE5's - valid Directory entries have// a first byte of 0..15 and the erase process just rewrote this with// 0xE5 (note that the name remained in tact - unlike MS-DOS).  We want// to scan all valid entries to build the allocation vector.  Since// entries are allocated by using the first available, sequentially,// if we use 0xF7 to erase a file, the first time we strike an 0xE5// flag, we know that no more valid files exist after this point -// resulting in a considerably shortened log process.// void CpmBdos::LogDisk (void){  XfReq *pX;    if (pCsv)    memset(pCsv, 0xff, nCks);  memset(pAlv, 0, (nDks + 7) / 8);  UINT16 nDirBls = ((nDir * 32) + nBls - 1) / nBls;  UINT16 nBlkVal = 0;  while (nDirBls--)    AllocBlk(nBlkVal++);  pX = new XfReq;  if (pX) {    pX->pFnCb = this;    pX->ivec  = LOG1;    pX->cmnd  = DD_READ;    pX->pDma  = (char*)DirBuf;    pX->pFcb  = new FCB;    if (NULL == pX->pFcb)      delete pX;    else {      memset((char*)pX->pFcb, 0, sizeof(FCB));      pX->pFcb->nCurRec = nDir / 4;      DirScanPrep(pX->pFcb, &pX->nTrk, &pX->nSec);      message msg(nId, 0, 0, (void*)pX);      pTx->PostMsg(nChan, &msg);    }  }}///////////// This is the directory scan log-in loop. We arrive here each time// another DIR block has been read to scan for current files, setting// the Allocation vectors from the individual entry maps and// calculating the checksums for all (checked) dir entries until// no more files CAN be found, or all DIR blocks have been scanned.//void CpmBdos::LogDisk1 (XfReq *pX){  if (FALSE == pX->bRes)    DoRetry(pX);  else {    int idx;    for (idx = 0; idx < 4; idx++) {      DIRENT *pDir = (DIRENT*)(pX->pDma + (idx * 32));      if (0xE5 == pDir->nUsrNmbr)        break;      if (0xF7 != pDir->nUsrNmbr)        for (int jdx = 0; jdx < 16; jdx++) {          if (0 == pDir->nAllocBlk[jdx])            break;          AllocBlk((UINT16)pDir->nAllocBlk[jdx]);        }    }    if ((idx < 4) || (--(pX->pFcb->nCurRec) <= 0)) {      delete pX->pFcb;      pX->pFcb = NULL;    }    else {      ++(pX->pFcb->de.nRes2);      if (pX->pFcb->de.nRes2 >= (nBls / 128)) {        ++(pX->pFcb->de.nRes1);        pX->pFcb->de.nRes2 = 0;      }      BlkDecode(pX->pFcb->de.nRes1, pX->pFcb->de.nRes2,              &(pX->nTrk), &(pX->nSec));      ReGenerate(pX, 1);    }  }}//////////////////// This is the loop point for "Open" operations.  If the read was Ok, we// scan the Dir record, terminating "not found" if we encounter an 0xE5// flag.  Valid names are matched against the required name (Wild cards// ARE permitted) and the actual disk dir entry copied over the user's// FCB on first match (the extent is also matched).//void CpmBdos::Open1 (XfReq *pX){  if (FALSE == pX->bRes)    DoRetry(pX);  else {    int idx;    for (idx = 0; idx < 4; idx++) {      DIRENT *pDir = (DIRENT*)(pX->pDma + (idx * 32));      if (0xE5 == pDir->nUsrNmbr) {        Open2(pX, FALSE);        return;      }      if ((0xF7 != pDir->nUsrNmbr) && (pDir->nExtent == pX->pFcb->de.nExtent)) {        int jdx;        for (jdx = 0; jdx < FILENAME_LEN; jdx++)          if ((pX->pFcb->de.fname[jdx] != pDir->fname[jdx]) &&              (pX->pFcb->de.fname[jdx] != '?'))            break;        if (jdx == FILENAME_LEN) {          pX->pFcb->de.nRes1 = idx * 32;          pX->pFcb->de.nRes2 = 0;          pX->pFcb->nCurRec = 0;          Open2(pX, TRUE);          return;        }      }    }    if (--pX->pFcb->nCurRec <= 0)      Open2(pX, FALSE);    else {      ++(pX->pFcb->de.nRes2);      if (pX->pFcb->de.nRes2 >= (nBls / 128)) {        ++(pX->pFcb->de.nRes1);        pX->pFcb->de.nRes2 = 0;      }      BlkDecode(pX->pFcb->de.nRes1, pX->pFcb->de.nRes2,              &(pX->nTrk), &(pX->nSec));      ReGenerate(pX, 2);      //message msg(nId, 2, 2, (void*)pX);      //pTx->PostMsg(nChan, &msg);    }  }}//////////////// This is the end of the "Open" process and we've either found the file// or we've not - so it's time to clean up and reply to the request.//// We must check for chained XfReq blocks, because we may have been// called to open the next extent of a file during a Read or Write, or// scan for the file before doing a Creat.  In such cases, control gets// passed via the usual <NxtFunc> member of the attached request.  If// there is no attached request, we message back to the original request// process ID via Kernel post.//void CpmBdos::Open2 (XfReq *pX, BOOL bFound){  if (NULL == pX->pNxt) {    char *cp1 = (char*)&pX->pFcb->de;    char *cp2 = (char*)pX->pDma+pX->pFcb->de.nRes1;    memcpy(cp1, cp2, sizeof(DIRENT));    pX->pFcb->nCurRec = 0;    message msg(nId, (bFound ? FS_Ok : FS_NotFound));    pTx->PostMsg(pX->uProc, &msg);  }  else {    XfReq *pInner = pX->pNxt;    pInner->bRes = bFound;    switch (pInner->ivec) {      case CLOSE1:      case REMOV1:        pInner->nTrk = pX->nTrk;        pInner->nSec = pX->nSec;        pInner->nSid = pX->nSid;        pInner->pDma = pX->pDma;        pInner->pFcb->de.nRes1 = pX->pFcb->de.nRes1;        break;    default:      memcpy((char*)&pInner->pFcb->de, (char*)&pX->pFcb->de, sizeof(DIRENT));      pInner->pFcb->nCurRec = 0;    }    delete pX->pFcb;    NxtFunc(pInner);  }}///////////////// Arriving here, we have scanned the directory for the file and the// result sits in bRes: FALSE means the file does not exists, so we // need to scan for the first free directory entry of the disk.//void CpmBdos::Creat1 (XfReq *pX){  if (pX->bRes) {    message msg(nId, FS_Exists);    pTx->PostMsg(pX->uProc, &msg);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -