Skip to content

Commit

Permalink
[#6] Browsing raw sector data in hexa-mode (Part 57: Introduction and…
Browse files Browse the repository at this point in the history
… usage of CImage::IsSectorDataReady method to indicate eventual delay in querying given sector's data)
  • Loading branch information
tomas-nestorovic committed Apr 3, 2022
1 parent 8ad047c commit bd30cf9
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 34 deletions.
31 changes: 28 additions & 3 deletions Main/src/CapsBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,9 +601,9 @@
else if (pis->id==sectorId) // Sector IDs are equal
break;
// . if Sector with given ID not found in the Track, we are done
*outBufferLengths++=GetUsableSectorLength(sectorId.lengthCode); // e.g. Sector with LengthCode 167 has no data
if (!n){
*outBufferData++=nullptr, *outFdcStatuses++=TFdcStatus::SectorNotFound;
outBufferLengths++;
continue;
}
// . setting initial Revolution
Expand All @@ -612,7 +612,11 @@
pis->currentRevolution=pis->dirtyRevolution; // modified Revolution is obligatory for any subsequent data requests
else if (rev<pis->nRevolutions)
pis->currentRevolution=rev; // wanted particular existing Revolution
else
else if (rev<Revolution::MAX){
*outFdcStatuses++=TFdcStatus::SectorNotFound; // wanted particular non-existent Revolution
*outBufferData++=nullptr;
continue;
}else
switch (rev){
default:
ASSERT(FALSE); // we shouldn't end up here!
Expand Down Expand Up @@ -646,7 +650,7 @@
// . returning (any) Data
*outFdcStatuses++=currRev->fdcStatus;
*outBufferData++=currRev->data;
*outBufferLengths++=GetUsableSectorLength(sectorId.lengthCode); // e.g. Sector with LengthCode 167 has no data
//*outBufferLengths++=... // already set above
}
else
invalidTrack:
Expand All @@ -655,6 +659,27 @@
::SetLastError( *--outBufferData ? ERROR_SUCCESS : ERROR_SECTOR_NOT_FOUND );
}

TDataStatus CCapsBase::IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const{
// True <=> specified Sector's data variation (Revolution) has been buffered, otherwise False
ASSERT( rev<Revolution::MAX );
EXCLUSIVELY_LOCK_THIS_IMAGE();
if (cyl<=capsImageInfo.maxcylinder && head<=capsImageInfo.maxhead) // does Track actually exist?
if (const PCInternalTrack pit=internalTracks[cyl][head]) // is Track scanned?
while (nSectorsToSkip<pit->nSectors){
const auto &ris=pit->sectors[nSectorsToSkip++];
if (ris.id==id)
if (rev>=ris.nRevolutions)
return TDataStatus::READY; // can't create another sample of the data (unlike in CFDD class where we have infinite # of trials), so here declaring "no data ready"
else if (ris.revolutions[rev].HasGoodDataReady())
return TDataStatus::READY_HEALTHY;
else if (ris.revolutions[rev].HasDataReady())
return TDataStatus::READY;
else
break;
}
return TDataStatus::NOT_READY;
}

Revolution::TType CCapsBase::GetDirtyRevolution(RCPhysicalAddress chs,BYTE nSectorsToSkip) const{
// returns the Revolution that has been marked as "dirty"
if (const PCInternalTrack pit=internalTracks[chs.cylinder][chs.head]){
Expand Down
4 changes: 4 additions & 0 deletions Main/src/CapsBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
CTrackReader::TProfile idEndProfile;
PSectorData data;
TFdcStatus fdcStatus;

inline bool HasDataReady() const{ return data!=nullptr || fdcStatus.DescribesMissingDam(); }
inline bool HasGoodDataReady() const{ return data!=nullptr && fdcStatus.IsWithoutError(); }
} revolutions[Revolution::MAX];
BYTE nRevolutions;
BYTE currentRevolution;
Expand Down Expand Up @@ -152,6 +155,7 @@
TSector ScanTrack(TCylinder cyl,THead head,Codec::PType pCodec=nullptr,PSectorId bufferId=nullptr,PWORD bufferLength=nullptr,PLogTime startTimesNanoseconds=nullptr,PBYTE pAvgGap3=nullptr) const override;
bool IsTrackScanned(TCylinder cyl,THead head) const override sealed;
void GetTrackData(TCylinder cyl,THead head,Revolution::TType rev,PCSectorId bufferId,PCBYTE bufferNumbersOfSectorsToSkip,TSector nSectors,PSectorData *outBufferData,PWORD outBufferLengths,TFdcStatus *outFdcStatuses) override;
TDataStatus IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const override;
Revolution::TType GetDirtyRevolution(RCPhysicalAddress chs,BYTE nSectorsToSkip) const override;
TStdWinError GetInsertedMediumType(TCylinder cyl,Medium::TType &rOutMediumType) const override;
TStdWinError SetMediumTypeAndGeometry(PCFormat pFormat,PCSide sideMap,TSector firstSectorNumber) override;
Expand Down
13 changes: 13 additions & 0 deletions Main/src/Dsk5.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,19 @@ formatError: ::SetLastError(ERROR_BAD_FORMAT);
::SetLastError( *--outBufferData ? ERROR_SUCCESS : ERROR_SECTOR_NOT_FOUND );
}

TDataStatus CDsk5::IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const{
// True <=> specified Sector's data variation (Revolution) has been buffered, otherwise False
ASSERT( rev<Revolution::MAX );
EXCLUSIVELY_LOCK_THIS_IMAGE();
if (const PTrackInfo ti=__findTrack__(cyl,head))
while (nSectorsToSkip<ti->nSectors){
const auto &si=ti->sectorInfo[nSectorsToSkip++];
if (si==id)
return si.HasGoodDataReady() ? TDataStatus::READY_HEALTHY : TDataStatus::READY;
}
return TDataStatus::NOT_READY;
}

TStdWinError CDsk5::MarkSectorAsDirty(RCPhysicalAddress chs,BYTE nSectorsToSkip,PCFdcStatus pFdcStatus){
// marks Sector on a given PhysicalAddress as "dirty", plus sets it the given FdcStatus; returns Windows standard i/o error
EXCLUSIVELY_LOCK_THIS_IMAGE();
Expand Down
2 changes: 2 additions & 0 deletions Main/src/Dsk5.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

bool operator==(const TSectorId &rSectorId) const;
bool __hasValidSectorLengthCode__() const;
inline bool HasGoodDataReady() const{ return fdcStatus.IsWithoutError(); }
} *PSectorInfo;

#pragma pack(1)
Expand Down Expand Up @@ -86,6 +87,7 @@
TSector ScanTrack(TCylinder cyl,THead head,Codec::PType pCodec=nullptr,PSectorId bufferId=nullptr,PWORD bufferLength=nullptr,PLogTime startTimesNanoseconds=nullptr,PBYTE pAvgGap3=nullptr) const override;
bool IsTrackScanned(TCylinder cyl,THead head) const override;
void GetTrackData(TCylinder cyl,THead head,Revolution::TType rev,PCSectorId bufferId,PCBYTE bufferNumbersOfSectorsToSkip,TSector nSectors,PSectorData *outBufferData,PWORD outBufferLengths,TFdcStatus *outFdcStatuses) override;
TDataStatus IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const override;
TStdWinError MarkSectorAsDirty(RCPhysicalAddress chs,BYTE nSectorsToSkip,PCFdcStatus pFdcStatus) override;
TStdWinError SetMediumTypeAndGeometry(PCFormat pFormat,PCSide sideMap,TSector firstSectorNumber) override;
bool EditSettings(bool initialEditing) override;
Expand Down
26 changes: 21 additions & 5 deletions Main/src/FDD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -934,10 +934,6 @@ error: switch (const TStdWinError err=::GetLastError()){
(Revolution::MAX-1)*sizeof(*rsi.revolutions)
);
rsi.nRevolutions--;
EXCLUSIVELY_LOCK(scannedTracks);
auto &info=scannedTracks.infos[(cyl<<1)+head];
info.bufferedRevs&=(1<<Revolution::MAX)-1; // keep only particular Revolution-related flags, resetting all special flags, e.g. whether AnyGood data exist
info.bufferedRevs>>=1;
}
// > attempt for the Data
auto &rev=rsi.revolutions[ rsi.currentRevolution=rsi.nRevolutions ]; // the last Revolution, below set empty
Expand All @@ -951,7 +947,7 @@ error: switch (const TStdWinError err=::GetLastError()){
// > checking if we already have healthy Data in the buffer
bool healthyDataExist=false;
for( rsi.currentRevolution=rsi.nRevolutions; rsi.currentRevolution>0; ) // check latest Revolution first
if ( healthyDataExist=rsi.revolutions[--rsi.currentRevolution].HasHealthyData() )
if ( healthyDataExist=rsi.revolutions[--rsi.currentRevolution].HasGoodDataReady() )
break;
if (healthyDataExist)
break;
Expand Down Expand Up @@ -1007,6 +1003,26 @@ error: switch (const TStdWinError err=::GetLastError()){
::SetLastError( outBufferData[nSectors-1] ? ERROR_SUCCESS : ERROR_SECTOR_NOT_FOUND );
}

TDataStatus CFDD::IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const{
// True <=> specified Sector's data variation (Revolution) has been buffered, otherwise False
ASSERT( rev<Revolution::MAX );
EXCLUSIVELY_LOCK_THIS_IMAGE();
if (const PCInternalTrack pit=__getScannedTrack__(cyl,head)) // Track has already been scanned
while (nSectorsToSkip<pit->nSectors){
const auto &ris=pit->sectors[nSectorsToSkip++];
if (ris.id==id)
if (rev>=ris.nRevolutions)
break;
else if (ris.revolutions[rev].HasGoodDataReady())
return TDataStatus::READY_HEALTHY;
else if (ris.revolutions[rev].HasDataReady())
return TDataStatus::READY;
else
break;
}
return TDataStatus::NOT_READY;
}

TStdWinError CFDD::MarkSectorAsDirty(RCPhysicalAddress chs,BYTE nSectorsToSkip,PCFdcStatus pFdcStatus){
// marks Sector with specified PhysicalAddress as "dirty", plus sets it the given FdcStatus; returns Windows standard i/o error
LOG_SECTOR_ACTION(&chs.sectorId,_T("TStdWinError CFDD::MarkSectorAsDirty"));
Expand Down
5 changes: 4 additions & 1 deletion Main/src/FDD.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
PSectorData data;
TFdcStatus fdcStatus;

inline bool HasHealthyData() const{ return data!=nullptr && fdcStatus.IsWithoutError(); }
inline bool HasDataReady() const{ return data!=nullptr || fdcStatus.DescribesMissingDam(); }
inline bool HasGoodDataReady() const{ return data!=nullptr && fdcStatus.IsWithoutError(); }
} revolutions[Revolution::MAX]; // a First-In-First-Out buffer of Revolutions
BYTE nRevolutions;
BYTE currentRevolution;
Expand All @@ -63,6 +64,7 @@

bool __isIdDuplicated__(PCSectorId id) const;
} *PInternalTrack;
typedef const TInternalTrack *PCInternalTrack;

static UINT AFX_CDECL __save_thread__(PVOID _pCancelableAction);
static UINT AFX_CDECL __determineControllerAndOneByteLatency_thread__(PVOID _pCancelableAction);
Expand Down Expand Up @@ -129,6 +131,7 @@
TSector ScanTrack(TCylinder cyl,THead head,Codec::PType pCodec=nullptr,PSectorId bufferId=nullptr,PWORD bufferLength=nullptr,PLogTime startTimesNanoseconds=nullptr,PBYTE pAvgGap3=nullptr) const override;
bool IsTrackScanned(TCylinder cyl,THead head) const override;
void GetTrackData(TCylinder cyl,THead head,Revolution::TType rev,PCSectorId bufferId,PCBYTE bufferNumbersOfSectorsToSkip,TSector nSectors,PSectorData *outBufferData,PWORD outBufferLengths,TFdcStatus *outFdcStatuses) override;
TDataStatus IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const override;
TStdWinError MarkSectorAsDirty(RCPhysicalAddress chs,BYTE nSectorsToSkip,PCFdcStatus pFdcStatus) override;
Revolution::TType GetDirtyRevolution(RCPhysicalAddress chs,BYTE nSectorsToSkip) const override;
TStdWinError GetInsertedMediumType(TCylinder cyl,Medium::TType &rOutMediumType) const override;
Expand Down
9 changes: 8 additions & 1 deletion Main/src/Image.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@

CString ToString() const;
} *PSectorId;
typedef const TSectorId *PCSectorId;
typedef const TSectorId *PCSectorId,&RCSectorId;

#pragma pack(1)
typedef const struct TPhysicalAddress sealed{
Expand All @@ -169,6 +169,12 @@
TTrack GetTrackNumber(THead nHeads) const;
} &RCPhysicalAddress;

enum TDataStatus{
NOT_READY =0, // querying data via CImage::GetTrackData may lead to delay (e.g. application freezes if called from main thread)
READY =1, // erroneous data are buffered, there will be no delay in calling CImage::GetTrackData
READY_HEALTHY=READY|2 // healthy data are buffered, there will be no delay in calling CImage::GetTrackData
};


#define FDC_ST1_END_OF_CYLINDER 128
// ^ FDC tried to access a sector beyond the final sector of the track (255D*). Will be set if TC is not issued after Read or Write Data command
Expand Down Expand Up @@ -668,6 +674,7 @@
PSectorData GetHealthySectorData(RCPhysicalAddress chs,PWORD sectorLength);
PSectorData GetHealthySectorData(RCPhysicalAddress chs);
PSectorData GetHealthySectorDataOfUnknownLength(TPhysicalAddress &rChs,PWORD sectorLength);
virtual TDataStatus IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const=0;
virtual TStdWinError MarkSectorAsDirty(RCPhysicalAddress chs,BYTE nSectorsToSkip,PCFdcStatus pFdcStatus)=0;
void MarkSectorAsDirty(RCPhysicalAddress chs);
virtual Revolution::TType GetDirtyRevolution(RCPhysicalAddress chs,BYTE nSectorsToSkip) const;
Expand Down
70 changes: 51 additions & 19 deletions Main/src/ImageFloppy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
// ctor
: n(0) , allScanned(false)
, dataTotalLength(0) {
::ZeroMemory( infos, sizeof(infos) );
}


Expand Down Expand Up @@ -115,21 +114,26 @@
ps->trackWorker.Suspend(); // again resumed via SetTrackScannerStatus method
// . first, processing a request to buffer a Track (if any)
if (ps->request.bufferEvent.Lock( scannedTracks.allScanned ? INFINITE : 2 )){
const TRequestParams req=ps->request;
ps->request.locker.Lock();
const TRequestParams req=ps->request;
ps->request.locker.Unlock();
TSectorId ids[FDD_SECTORS_MAX];
image->BufferTrackData(
req.track>>1, req.track&1, req.revolution,
ids, sectorIdAndPositionIdentity,
ps->__scanTrack__( req.track, ids, nullptr )
);
{ EXCLUSIVELY_LOCK(scannedTracks);
if (req.revolution<Revolution::MAX)
if (req.revolution!=Revolution::ALL_INTERSECTED)
// only particular Revolution wanted
scannedTracks.infos[req.track].bufferedRevs|=1<<req.revolution;
image->BufferTrackData(
req.track>>1, req.track&1, req.revolution,
ids, sectorIdAndPositionIdentity,
ps->__scanTrack__( req.track, ids, nullptr )
);
else
// all Revolutions wanted
scannedTracks.infos[req.track].bufferedRevs|=1<<Revolution::MAX;
} if (ps->workerStatus!=TScannerStatus::UNAVAILABLE) // should we terminate?
for( BYTE rev=std::min<BYTE>(Revolution::MAX,image->GetAvailableRevolutionCount()); rev-->0; )
image->BufferTrackData(
req.track>>1, req.track&1, (Revolution::TType)rev,
ids, sectorIdAndPositionIdentity,
ps->__scanTrack__( req.track, ids, nullptr )
);
if (ps->workerStatus!=TScannerStatus::UNAVAILABLE) // should we terminate?
ps->pParentHexaEditor->RepaintData(true); // True = immediate repainting
// . then, scanning the remaining Tracks (if not all yet scanned)
}else{
Expand Down Expand Up @@ -163,6 +167,7 @@
Revolution::TType revolution;
};
struct:public TRequestParams{
CCriticalSection locker;
CEvent bufferEvent;
} request;
struct{
Expand Down Expand Up @@ -224,8 +229,9 @@
// dtor
// . terminating the Worker
workerStatus=TScannerStatus::UNAVAILABLE;
{ EXCLUSIVELY_LOCK(request);
request.track=0; // zeroth Track highly likely already scanned, so there will be no waiting time
request.bufferEvent.SetEvent(); // releasing the eventually blocked Worker
} request.bufferEvent.SetEvent(); // releasing the eventually blocked Worker
::WaitForSingleObject( trackWorker, INFINITE );
}

Expand Down Expand Up @@ -389,8 +395,8 @@
}
void GetRecordInfo(int logPos,PINT pOutRecordStartLogPos,PINT pOutRecordLength,bool *pOutDataReady) override{
// retrieves the start logical position and length of the Record pointed to by the input LogicalPosition
TTrack track;
if (!__getPhysicalAddress__(logPos,&track,nullptr,nullptr))
TTrack track; BYTE iSector;
if (!__getPhysicalAddress__(logPos,&track,&iSector,nullptr))
return;
if (pOutRecordStartLogPos || pOutRecordLength){
WORD lengths[FDD_SECTORS_MAX];
Expand All @@ -403,16 +409,42 @@
*pOutRecordLength = lengths[nSectors];
}
if (pOutDataReady){
const BYTE mask=1<<std::min( revolution, Revolution::MAX );
const auto &scannedTracks=GetFloppyImage().scannedTracks;
EXCLUSIVELY_LOCK_SCANNED_TRACKS(); // commented out for smoother operation; may thus work with outdated values in ScannedTracks but that's ok!
if (!( *pOutDataReady=(scannedTracks.infos[track].bufferedRevs&mask)==mask ))
TSectorId ids[(TSector)-1];
__scanTrack__( track, ids, nullptr );
switch (revolution){
case Revolution::ANY_GOOD:
*pOutDataReady=true; // assumption (all Revolutions already attempted, none holding healthy data)
for( BYTE r=0; r<std::min<BYTE>(Revolution::MAX,image->GetAvailableRevolutionCount()); r++ )
if (const TDataStatus ds=image->IsSectorDataReady( track>>1, track&1, ids[iSector], iSector, (Revolution::TType)r )){
if (ds==TDataStatus::READY_HEALTHY){
*pOutDataReady=true;
break;
}
}else{
// this Revolution hasn't yet been queried for data, so there is a chance it contains healthy data
*pOutDataReady=false;
EXCLUSIVELY_LOCK(request);
request.revolution=Revolution::NONE; // to issue a duplicate request below
}
break;
case Revolution::ALL_INTERSECTED:
*pOutDataReady=true; // assumption (all Revolutions already attempted)
for( BYTE r=0; r<std::min<BYTE>(Revolution::MAX,image->GetAvailableRevolutionCount()); r++ )
*pOutDataReady&=image->IsSectorDataReady( track>>1, track&1, ids[iSector], iSector, (Revolution::TType)r )!=TDataStatus::NOT_READY;
break;
default:
*pOutDataReady=image->IsSectorDataReady( track>>1, track&1, ids[iSector], iSector, revolution );
break;
}
if (!*pOutDataReady){
// Sector not yet buffered and its data probably wanted - buffering them now
EXCLUSIVELY_LOCK(request);
if (request.track!=track || request.revolution!=revolution){ // Request not yet issued
request.track=track;
request.revolution=revolution;
request.bufferEvent.SetEvent();
}
}
}
}
LPCWSTR GetRecordLabelW(int logPos,PWCHAR labelBuffer,BYTE labelBufferCharsMax,PVOID param) const override{
Expand Down
4 changes: 0 additions & 4 deletions Main/src/ImageFloppy.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
#define IMAGEFLOPPY_H

class CFloppyImage:public CImage{
protected:
struct TScannedTracks sealed{
CCriticalSection locker;
struct{
WORD bufferedRevs; // bits mapped to individual Revolutions, e.g. bit 0 = Revolution::R0, etc.
} infos[FDD_CYLINDERS_MAX*2+1];
BYTE n;
bool allScanned;
#if _MFC_VER>=0x0A00
Expand Down
21 changes: 20 additions & 1 deletion Main/src/ImageRaw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@
}
}

bool CImageRaw::IsKnownSector(TCylinder cyl,THead head,RCSectorId id) const{
//
return id.cylinder==cyl
&&
id.side==sideMap[head]
&&
id.sector>=firstSectorNumber && id.sector-firstSectorNumber<nSectors && id.lengthCode==sectorLengthCode;
}

PSectorData CImageRaw::__getBufferedSectorData__(TCylinder cyl,THead head,PCSectorId sectorId) const{
// finds and returns buffered data of given Sector (or Null if not yet buffered; note that returning Null does NOT imply that the Sector doesn't exist in corresponding Track!)
if (const PSectorData cylinderData=(PSectorData)bufferOfCylinders[cyl])
Expand Down Expand Up @@ -215,7 +224,7 @@
if (cyl<nCylinders && head<nHeads)
while (nSectors>0){
const TSectorId sectorId=*bufferId;
if (sectorId.cylinder==cyl && sectorId.side==sideMap[head] && sectorId.sector>=firstSectorNumber && sectorId.sector-firstSectorNumber<this->nSectors && sectorId.lengthCode==sectorLengthCode){
if (IsKnownSector( cyl, head, sectorId )){
// Sector with the given ID found in the Track
if (const PSectorData bufferedData=__getBufferedSectorData__(cyl,head,&sectorId)){
// Sector's Data successfully retrieved from the buffer
Expand Down Expand Up @@ -262,6 +271,16 @@
::SetLastError( *--outBufferData ? err : ERROR_SECTOR_NOT_FOUND );
}

TDataStatus CImageRaw::IsSectorDataReady(TCylinder cyl,THead head,RCSectorId id,BYTE nSectorsToSkip,Revolution::TType rev) const{
// True <=> specified Sector's data variation (Revolution) has been buffered, otherwise False
ASSERT( rev<Revolution::MAX );
EXCLUSIVELY_LOCK_THIS_IMAGE();
if (cyl<nCylinders && head<nHeads)
if (IsKnownSector( cyl, head, id ))
return TDataStatus::READY_HEALTHY;
return TDataStatus::NOT_READY;
}

TStdWinError CImageRaw::MarkSectorAsDirty(RCPhysicalAddress chs,BYTE,PCFdcStatus pFdcStatus){
// marks Sector with given PhysicalAddress as "dirty", plus sets it the given FdcStatus; returns Windows standard i/o error
EXCLUSIVELY_LOCK_THIS_IMAGE();
Expand Down
Loading

0 comments on commit bd30cf9

Please sign in to comment.