Write support for WOZ1/WOZ2 images (#756)

Also:
- Allow creation of a blank (WOZ2) image
- multi-zip support extended to scan for the first valid image (useful for most woz-a-day zips which have at least 2 entries and were previously failing)
This commit is contained in:
TomCh 2020-02-09 21:23:15 +00:00 committed by GitHub
parent d07558b610
commit 4956957ca1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 757 additions and 314 deletions

View File

@ -282,6 +282,12 @@ void Disk2InterfaceCard::ReadTrack(const int drive, ULONG uExecutedCycles)
if (ImagePhaseToTrack(pFloppy->m_imagehandle, pDrive->m_phasePrecise, false) >= ImageGetNumTracks(pFloppy->m_imagehandle))
{
_ASSERT(0); // What can cause this? Add a comment to replace this assert.
// Boot with DOS 3.3 Master in D1
// Create a blank disk in D2
// INIT HELLO,D2
// RUN HELLO
// F2 to reboot DOS 3.3 Master
// RUN HELLO,D2
pFloppy->m_trackimagedata = false;
return;
}
@ -729,17 +735,6 @@ void Disk2InterfaceCard::NotifyInvalidImage(const int drive, LPCTSTR pszImageFil
pszImageFilename);
break;
case eIMAGE_ERROR_UNSUPPORTED_MULTI_ZIP:
StringCbPrintf(
szBuffer,
MAX_PATH + 128,
TEXT("Unable to use the file %s\nbecause the ")
TEXT("first file (%s) in this multi-zip archive is not recognized.\n")
TEXT("Try unzipping and using the disk images directly.\n"),
pszImageFilename,
m_floppyDrive[drive].m_disk.m_strFilenameInZip.c_str());
break;
case eIMAGE_ERROR_GZ:
case eIMAGE_ERROR_ZIP:
StringCbPrintf(
@ -984,24 +979,18 @@ void Disk2InterfaceCard::ResetLogicStateSequencer(void)
m_shiftReg = 0;
m_latchDelay = 0;
m_resetSequencer = true;
m_writeStarted = false;
m_dbgLatchDelayedCnt = 0;
}
void Disk2InterfaceCard::UpdateBitStreamPositionAndDiskCycle(const ULONG uExecutedCycles)
UINT Disk2InterfaceCard::GetBitCellDelta(const ULONG uExecutedCycles)
{
FloppyDisk& floppy = m_floppyDrive[m_currDrive].m_disk;
CpuCalcCycles(uExecutedCycles);
const UINT bitCellDelta = GetBitCellDelta(ImageGetOptimalBitTiming(floppy.m_imagehandle));
UpdateBitStreamPosition(floppy, bitCellDelta);
m_diskLastCycle = g_nCumulativeCycles;
}
UINT Disk2InterfaceCard::GetBitCellDelta(const BYTE optimalBitTiming)
{
FloppyDisk& floppy = m_floppyDrive[m_currDrive].m_disk;
const BYTE optimalBitTiming = ImageGetOptimalBitTiming(floppy.m_imagehandle);
// NB. m_extraCycles is needed to retain accuracy:
// . Read latch #1: 0-> 9: cycleDelta= 9, bitCellDelta=2, extraCycles=1
// . Read latch #2: 9->20: cycleDelta=11, bitCellDelta=2, extraCycles=3
@ -1010,9 +999,9 @@ UINT Disk2InterfaceCard::GetBitCellDelta(const BYTE optimalBitTiming)
#if 0
if (optimalBitTiming == 32)
{
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - m_diskLastCycle) + (BYTE) m_extraCycles;
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - m_diskLastCycle) + (BYTE) floppy.m_extraCycles;
bitCellDelta = cycleDelta / 4; // DIV 4 for 4us per bit-cell
m_extraCycles = cycleDelta & 3; // MOD 4 : remainder carried forward for next time
floppy.m_extraCycles = cycleDelta & 3; // MOD 4 : remainder carried forward for next time
}
else
#endif
@ -1022,6 +1011,11 @@ UINT Disk2InterfaceCard::GetBitCellDelta(const BYTE optimalBitTiming)
bitCellDelta = (UINT) floor( cycleDelta / bitTime );
floppy.m_extraCycles = (double)cycleDelta - ((double)bitCellDelta * bitTime);
}
// NB. actual m_diskLastCycle for the last bitCell is minus floppy.m_extraCycles
// - but don't need this value; and it's correctly accounted for in this function.
m_diskLastCycle = g_nCumulativeCycles;
return bitCellDelta;
}
@ -1036,6 +1030,8 @@ void Disk2InterfaceCard::UpdateBitStreamPosition(FloppyDisk& floppy, const ULONG
floppy.m_bitOffset %= floppy.m_bitCount;
UpdateBitStreamOffsets(floppy);
m_resetSequencer = false;
}
void Disk2InterfaceCard::UpdateBitStreamOffsets(FloppyDisk& floppy)
@ -1045,8 +1041,28 @@ void Disk2InterfaceCard::UpdateBitStreamOffsets(FloppyDisk& floppy)
floppy.m_bitMask = 1 << remainder;
}
void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles)
__forceinline void Disk2InterfaceCard::IncBitStream(FloppyDisk& floppy)
{
floppy.m_bitMask >>= 1;
if (!floppy.m_bitMask)
{
floppy.m_bitMask = 1 << 7;
floppy.m_byte++;
}
floppy.m_bitOffset++;
if (floppy.m_bitOffset == floppy.m_bitCount)
{
floppy.m_bitMask = 1 << 7;
floppy.m_bitOffset = 0;
floppy.m_byte = 0;
}
}
void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, ULONG uExecutedCycles)
{
_ASSERT(m_seqFunc.function != dataShiftWrite);
FloppyDrive& drive = m_floppyDrive[m_currDrive];
FloppyDisk& floppy = drive.m_disk;
@ -1067,14 +1083,11 @@ void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYT
if (!drive.m_spinning) // GH#599
return;
CpuCalcCycles(uExecutedCycles);
// Skipping forward a large amount of bitcells means the bitstream will very likely be out-of-sync.
// The first 1-bit will produce a latch nibble, and this 1-bit is unlikely to be the nibble's high bit.
// So we need to ensure we run enough bits through the sequencer to re-sync.
// NB. For Planetfall 13 bitcells(NG) / 14 bitcells(OK)
const UINT significantBitCells = 50; // 5x 10-bit sync FF nibbles
UINT bitCellDelta = GetBitCellDelta(ImageGetOptimalBitTiming(floppy.m_imagehandle));
UINT bitCellDelta = GetBitCellDelta(uExecutedCycles);
UINT bitCellRemainder;
if (bitCellDelta <= significantBitCells)
@ -1092,20 +1105,21 @@ void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYT
drive.m_headWindow = 0;
}
// NB. actual m_diskLastCycle for the last bitCell is minus floppy.m_extraCycles
// - but don't need this value; and it's correctly accounted for in GetBitCellDelta()
m_diskLastCycle = g_nCumulativeCycles;
if (!bWrite)
{
if (m_seqFunc.function != readSequencing)
{
_ASSERT(m_seqFunc.function == checkWriteProtAndInitWrite);
UpdateBitStreamPosition(floppy, bitCellRemainder);
return;
}
DataLatchReadWOZ(pc, addr, bitCellRemainder);
}
else
{
DataLatchWriteWOZ(pc, addr, d, bitCellRemainder);
_ASSERT(m_seqFunc.function == dataLoadWrite);
DataLoadWriteWOZ(pc, addr, bitCellRemainder);
}
// Show track status (GH#201) - NB. Prevent flooding of forcing UI to redraw!!!
@ -1147,20 +1161,7 @@ void Disk2InterfaceCard::DataLatchReadWOZ(WORD pc, WORD addr, UINT bitCellRemain
BYTE outputBit = (drive.m_headWindow & 0xf) ? (drive.m_headWindow >> 1) & 1
: (rand() < ((RAND_MAX * 3) / 10)) ? 1 : 0; // ~30% chance of a 1 bit (Ref: WOZ-2.0)
floppy.m_bitMask >>= 1;
if (!floppy.m_bitMask)
{
floppy.m_bitMask = 1 << 7;
floppy.m_byte++;
}
floppy.m_bitOffset++;
if (floppy.m_bitOffset == floppy.m_bitCount)
{
floppy.m_bitMask = 1 << 7;
floppy.m_bitOffset = 0;
floppy.m_byte = 0;
}
IncBitStream(floppy);
if (m_resetSequencer)
{
@ -1238,17 +1239,64 @@ void Disk2InterfaceCard::DataLatchReadWOZ(WORD pc, WORD addr, UINT bitCellRemain
#endif
}
void Disk2InterfaceCard::DataLatchWriteWOZ(WORD pc, WORD addr, BYTE d, UINT bitCellRemainder)
void Disk2InterfaceCard::DataLoadWriteWOZ(WORD pc, WORD addr, UINT bitCellRemainder)
{
_ASSERT(m_seqFunc.writeMode);
_ASSERT(m_seqFunc.function == dataLoadWrite);
FloppyDrive& drive = m_floppyDrive[m_currDrive];
FloppyDisk& floppy = drive.m_disk;
if (!floppy.m_bWriteProtected)
if (floppy.m_bWriteProtected)
{
//TODO
_ASSERT(0); // Must be a bug in the 6502 code for this to occur!
UpdateBitStreamPosition(floppy, bitCellRemainder);
return;
}
if (!m_writeStarted)
UpdateBitStreamPosition(floppy, bitCellRemainder); // skip over bitCells before switching to write mode
m_writeStarted = true;
#if LOG_DISK_WOZ_LOADWRITE
LOG_DISK("load shiftReg with %02X (was: %02X)\n", m_floppyLatch, m_shiftReg);
#endif
m_shiftReg = m_floppyLatch;
}
void Disk2InterfaceCard::DataShiftWriteWOZ(WORD pc, WORD addr, ULONG uExecutedCycles)
{
_ASSERT(m_seqFunc.function == dataShiftWrite);
FloppyDrive& drive = m_floppyDrive[m_currDrive];
FloppyDisk& floppy = drive.m_disk;
const UINT bitCellRemainder = GetBitCellDelta(uExecutedCycles);
if (floppy.m_bWriteProtected)
{
_ASSERT(0); // Must be a bug in the 6502 code for this to occur!
UpdateBitStreamPosition(floppy, bitCellRemainder);
return;
}
#if LOG_DISK_WOZ_SHIFTWRITE
LOG_DISK("T$%02X, bitOffset=%04X: %02X (%d bits)\n", drive.m_phase/2, floppy.m_bitOffset, m_shiftReg, bitCellRemainder);
#endif
for (UINT i = 0; i < bitCellRemainder; i++)
{
BYTE outputBit = m_shiftReg & 0x80;
m_shiftReg <<= 1;
BYTE n = floppy.m_trackimage[floppy.m_byte];
n &= ~floppy.m_bitMask;
if (outputBit) n |= floppy.m_bitMask;
floppy.m_trackimage[floppy.m_byte] = n;
IncBitStream(floppy);
}
floppy.m_trackimagedirty = true;
}
//===========================================================================
@ -1485,7 +1533,9 @@ bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilen
void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG uExecutedCycles)
{
// NB. m_seqFunc.function == checkWriteProtAndInitWrite or shiftWrite (both OK)
// NB. Only reads in LOAD mode can issue the SR (shift write-protect) operation - UTAIIe page 9-20, fig 9.11
// But STA $C08D,X (no PX) does a read from $C08D+X, followed by the write to $C08D+X
// So just want to ignore: STA $C0ED or eg. STA $BFFF,X (PX, X=$EE)
// Don't change latch if drive off after 1 second drive-off delay (UTAIIe page 9-13)
// "DRIVES OFF forces the data register to hold its present state." (UTAIIe page 9-12)
@ -1500,20 +1550,27 @@ void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE
// the write protect switch would still be read correctly" (UTAIIe page 9-21)
// . Sequencer "SR" (Shift Right) command only loads QA (bit7) of data register (UTAIIe page 9-21)
// . A read or write will shift 'write protect' in QA.
if (m_floppyDrive[m_currDrive].m_disk.m_bWriteProtected)
FloppyDisk& floppy = m_floppyDrive[m_currDrive].m_disk;
if (floppy.m_bWriteProtected)
m_floppyLatch |= 0x80;
else
m_floppyLatch &= 0x7F;
if (ImageIsWOZ(m_floppyDrive[m_currDrive].m_disk.m_imagehandle))
if (m_writeStarted) // Prevent ResetLogicStateSequencer() from resetting m_writeStarted
return;
if (ImageIsWOZ(floppy.m_imagehandle))
{
#if LOG_DISK_NIBBLES_READ
CpuCalcCycles(uExecutedCycles);
LOG_DISK("%08X: reset LSS: ~PC=%04X\r\n", (UINT32)g_nCumulativeCycles, regs.pc);
#endif
const UINT bitCellDelta = GetBitCellDelta(uExecutedCycles);
UpdateBitStreamPosition(floppy, bitCellDelta); // Fix E7-copy protection
// UpdateBitStreamPosition() must be done below ResetLSS, as the former clears m_resetSequencer. (Commando.woz is sensitive to this)
ResetLogicStateSequencer(); // reset sequencer (UTAIIe page 9-21)
// m_latchDelay = 7; // TODO: Treat like a regular $C0EC latch load?
UpdateBitStreamPositionAndDiskCycle(uExecutedCycles); // Fix E7-copy protection
}
}
@ -1688,6 +1745,9 @@ void Disk2InterfaceCard::SetSequencerFunction(WORD addr)
case 2: m_seqFunc.loadMode = 0; break; // $C08C,X (sequence addr A3 input)
case 3: m_seqFunc.loadMode = 1; break; // $C08D,X (sequence addr A3 input)
}
if (!m_seqFunc.writeMode)
m_writeStarted = false;
}
BYTE __stdcall Disk2InterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
@ -1698,6 +1758,9 @@ BYTE __stdcall Disk2InterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE
ImageInfo* pImage = pCard->m_floppyDrive[pCard->m_currDrive].m_disk.m_imagehandle;
bool isWOZ = ImageIsWOZ(pImage);
if (isWOZ && pCard->m_seqFunc.function == dataShiftWrite) // Occurs at end of sector write ($C0EE)
pCard->DataShiftWriteWOZ(pc, addr, nExecutedCycles); // Finish any previous write
pCard->SetSequencerFunction(addr);
switch (addr & 0xF)
@ -1723,8 +1786,8 @@ BYTE __stdcall Disk2InterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE
// only even addresses return the latch (UTAIIe Table 9.1)
if (!(addr & 1))
{
if (isWOZ)
pCard->DataLatchReadWriteWOZ(pc, addr, bWrite, d, nExecutedCycles);
if (isWOZ && pCard->m_seqFunc.function != dataShiftWrite)
pCard->DataLatchReadWriteWOZ(pc, addr, bWrite, nExecutedCycles);
return pCard->m_floppyLatch;
}
@ -1740,6 +1803,9 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
ImageInfo* pImage = pCard->m_floppyDrive[pCard->m_currDrive].m_disk.m_imagehandle;
bool isWOZ = ImageIsWOZ(pImage);
if (isWOZ && pCard->m_seqFunc.function == dataShiftWrite)
pCard->DataShiftWriteWOZ(pc, addr, nExecutedCycles); // Finish any previous write
pCard->SetSequencerFunction(addr);
switch (addr & 0xF)
@ -1763,13 +1829,12 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
}
// any address writes the latch via sequencer LD command (74LS323 datasheet)
// if (pCard->m_seqFunc.writeMode /* && m_seqFunc.loadMode */)
if (pCard->m_seqFunc.function == dataLoadWrite)
{
pCard->m_floppyLatch = d;
if (isWOZ)
pCard->DataLatchReadWriteWOZ(pc, addr, bWrite, d, nExecutedCycles);
pCard->DataLatchReadWriteWOZ(pc, addr, bWrite, nExecutedCycles);
}
return 0;

View File

@ -185,12 +185,13 @@ private:
void WriteTrack(const int drive);
const std::string & DiskGetFullPathName(const int drive);
void ResetLogicStateSequencer(void);
void UpdateBitStreamPositionAndDiskCycle(const ULONG uExecutedCycles);
UINT GetBitCellDelta(const BYTE optimalBitTiming);
UINT GetBitCellDelta(const ULONG uExecutedCycles);
void UpdateBitStreamPosition(FloppyDisk& floppy, const ULONG bitCellDelta);
void UpdateBitStreamOffsets(FloppyDisk& floppy);
__forceinline void IncBitStream(FloppyDisk& floppy);
void DataLatchReadWOZ(WORD pc, WORD addr, UINT bitCellRemainder);
void DataLatchWriteWOZ(WORD pc, WORD addr, BYTE d, UINT bitCellRemainder);
void DataLoadWriteWOZ(WORD pc, WORD addr, UINT bitCellRemainder);
void DataShiftWriteWOZ(WORD pc, WORD addr, ULONG uExecutedCycles);
void SetSequencerFunction(WORD addr);
void DumpSectorWOZ(FloppyDisk floppy);
void DumpTrackWOZ(FloppyDisk floppy);
@ -206,7 +207,7 @@ private:
void __stdcall ControlMotor(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall Enable(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles);
void __stdcall DataLatchReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles);
void __stdcall DataLatchReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, ULONG uExecutedCycles);
void __stdcall LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG);
void __stdcall SetReadMode(WORD, WORD, BYTE, BYTE, ULONG);
void __stdcall SetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles);
@ -240,8 +241,9 @@ private:
BYTE m_shiftReg;
int m_latchDelay;
bool m_resetSequencer;
bool m_writeStarted;
enum SEQFUNC {readSequencing=0, checkWriteProtAndInitWrite, dataShiftWrite, dataLoadWrite}; // UTAIIe 9-14
enum SEQFUNC {readSequencing=0, dataShiftWrite, checkWriteProtAndInitWrite, dataLoadWrite}; // UTAIIe 9-14
union SEQUENCER_FUNCTION
{
struct

View File

@ -63,7 +63,7 @@ ImageError_e ImageOpen( const std::string & pszImageFilename,
ImageError_e Err = pImageInfo->pImageHelper->Open(pszImageFilename.c_str(), pImageInfo, bCreateIfNecessary, strFilenameInZip);
if (Err != eIMAGE_ERROR_NONE)
{
ImageClose(*ppImageInfo, true);
ImageClose(*ppImageInfo);
*ppImageInfo = NULL;
return Err;
}
@ -83,9 +83,6 @@ ImageError_e ImageOpen( const std::string & pszImageFilename,
pImageInfo->uNumTracks = sg_DiskImageHelper.GetNumTracksInImage(pImageInfo->pImageType);
for (UINT uTrack = 0; uTrack < pImageInfo->uNumTracks; uTrack++)
pImageInfo->ValidTrack[uTrack] = (pImageInfo->uImageSize > 0) ? 1 : 0;
*pWriteProtected = pImageInfo->bWriteProtected;
return eIMAGE_ERROR_NONE;
@ -93,25 +90,9 @@ ImageError_e ImageOpen( const std::string & pszImageFilename,
//===========================================================================
void ImageClose(ImageInfo* const pImageInfo, const bool bOpenError /*=false*/)
void ImageClose(ImageInfo* const pImageInfo)
{
bool bDeleteFile = false;
if (!bOpenError)
{
for (UINT uTrack = 0; uTrack < pImageInfo->uNumTracks; uTrack++)
{
if (!pImageInfo->ValidTrack[uTrack])
{
// TODO: Comment using info from this URL:
// http://groups.google.de/group/comp.emulators.apple2/msg/7a1b9317e7905152
bDeleteFile = true;
break;
}
}
}
pImageInfo->pImageHelper->Close(pImageInfo, bDeleteFile);
pImageInfo->pImageHelper->Close(pImageInfo);
delete pImageInfo;
}
@ -162,7 +143,7 @@ void ImageReadTrack( ImageInfo* const pImageInfo,
const UINT track = pImageInfo->pImageType->PhaseToTrack(phase);
if (pImageInfo->pImageType->AllowRW() && pImageInfo->ValidTrack[track])
if (pImageInfo->pImageType->AllowRW())
{
pImageInfo->pImageType->Read(pImageInfo, phase, pTrackImageBuffer, pNibbles, pBitCount, enhanceDisk);
}
@ -190,7 +171,14 @@ void ImageWriteTrack( ImageInfo* const pImageInfo,
if (pImageInfo->pImageType->AllowRW() && !pImageInfo->bWriteProtected)
{
pImageInfo->pImageType->Write(pImageInfo, phase, pTrackImageBuffer, nNibbles);
pImageInfo->ValidTrack[track] = 1;
eImageType imageType = pImageInfo->pImageType->GetType();
if (imageType == eImageWOZ1 || imageType == eImageWOZ2)
{
DWORD dummy;
bool res = sg_DiskImageHelper.WOZUpdateInfo(pImageInfo, dummy);
_ASSERT(res);
}
}
}
@ -234,7 +222,7 @@ bool ImageIsWriteProtected(ImageInfo* const pImageInfo)
bool ImageIsMultiFileZip(ImageInfo* const pImageInfo)
{
return pImageInfo ? (pImageInfo->uNumEntriesInZip > 1) : false;
return pImageInfo ? (pImageInfo->uNumValidImagesInZip > 1) : false;
}
const std::string & ImageGetPathname(ImageInfo* const pImageInfo)

View File

@ -51,7 +51,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
eIMAGE_ERROR_GZ,
eIMAGE_ERROR_ZIP,
eIMAGE_ERROR_REJECTED_MULTI_ZIP,
eIMAGE_ERROR_UNSUPPORTED_MULTI_ZIP,
eIMAGE_ERROR_UNABLE_TO_OPEN,
eIMAGE_ERROR_UNABLE_TO_OPEN_GZ,
eIMAGE_ERROR_UNABLE_TO_OPEN_ZIP,
@ -66,7 +65,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
struct ImageInfo;
ImageError_e ImageOpen(const std::string & pszImageFilename, ImageInfo** ppImageInfo, bool* pWriteProtected, const bool bCreateIfNecessary, std::string& strFilenameInZip, const bool bExpectFloppy=true);
void ImageClose(ImageInfo* const pImageInfo, const bool bOpenError=false);
void ImageClose(ImageInfo* const pImageInfo);
BOOL ImageBoot(ImageInfo* const pImageInfo);
void ImageDestroy(void);
void ImageInitialize(void);

File diff suppressed because it is too large Load Diff

View File

@ -31,11 +31,11 @@ struct ImageInfo
std::string szFilenameInZip;
zip_fileinfo zipFileInfo;
UINT uNumEntriesInZip;
UINT uNumValidImagesInZip;
// Floppy only
BYTE ValidTrack[TRACKS_MAX];
UINT uNumTracks;
BYTE* pImageBuffer;
BYTE* pTrackMap; // WOZ only
BYTE* pWOZTrackMap; // WOZ only (points into pImageBuffer)
BYTE optimalBitTiming; // WOZ only
UINT maxNibblesPerTrack;
@ -73,6 +73,7 @@ public:
virtual const char* GetCreateExtensions(void) = 0;
virtual const char* GetRejectExtensions(void) = 0;
bool WriteImageHeader(ImageInfo* pImageInfo, LPBYTE pHdr, const UINT hdrSize);
void SetVolumeNumber(const BYTE uVolumeNumber) { m_uVolumeNumber = uVolumeNumber; }
bool IsValidImageSize(const DWORD uImageSize);
@ -88,6 +89,7 @@ protected:
bool WriteTrack(ImageInfo* pImageInfo, const int nTrack, LPBYTE pTrackBuffer, const UINT uTrackSize);
bool ReadBlock(ImageInfo* pImageInfo, const int nBlock, LPBYTE pBlockBuffer);
bool WriteBlock(ImageInfo* pImageInfo, const int nBlock, LPBYTE pBlockBuffer);
bool WriteImageData(ImageInfo* pImageInfo, LPBYTE pSrcBuffer, const UINT uSrcSize, const long offset);
LPBYTE Code62(int sector);
void Decode62(LPBYTE imageptr);
@ -134,6 +136,10 @@ private:
#pragma pack(push)
#pragma pack(1) // Ensure Header2IMG & WOZ structs are packed
#pragma warning(push)
#pragma warning(disable: 4200) // Allow zero-sized array in struct
class C2IMGHelper : public CHdrHelper
{
public:
@ -200,10 +206,15 @@ public:
virtual ~CWOZHelper(void) {}
virtual eDetectResult DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD& dwOffset) { _ASSERT(0); return eMismatch; }
virtual UINT GetMaxHdrSize(void) { return sizeof(WOZHeader); }
eDetectResult ProcessChunks(const LPBYTE pImage, const DWORD dwImageSize, DWORD& dwOffset, BYTE*& pTrackMap);
eDetectResult ProcessChunks(ImageInfo* pImageInfo, DWORD& dwOffset);
bool IsWriteProtected(void) { return m_pInfo->v1.writeProtected == 1; }
BYTE GetOptimalBitTiming(void) { return (m_pInfo->v1.version >= 2) ? m_pInfo->optimalBitTiming : CWOZHelper::InfoChunkv2::optimalBitTiming5_25; }
UINT GetMaxNibblesPerTrack(void) { return (m_pInfo->v1.version >= 2) ? m_pInfo->largestTrack*CWOZHelper::BLOCK_SIZE : CWOZHelper::WOZ1_TRACK_SIZE; }
BYTE GetOptimalBitTiming(void) { return (m_pInfo->v1.version >= 2) ? m_pInfo->optimalBitTiming : InfoChunkv2::optimalBitTiming5_25; }
UINT GetMaxNibblesPerTrack(void) { return (m_pInfo->v1.version >= 2) ? m_pInfo->largestTrack*CWOZHelper::BLOCK_SIZE : WOZ1_TRACK_SIZE; }
void InvalidateInfo(void) { m_pInfo = NULL; }
BYTE* CreateEmptyDisk(DWORD& size);
#if _DEBUG
BYTE* CreateEmptyDiskv1(DWORD& size);
#endif
static const UINT32 ID1_WOZ1 = '1ZOW'; // 'WOZ1'
static const UINT32 ID1_WOZ2 = '2ZOW'; // 'WOZ2'
@ -217,10 +228,24 @@ public:
};
static const UINT32 MAX_TRACKS_5_25 = 40;
static const UINT32 MAX_QUARTER_TRACKS_5_25 = MAX_TRACKS_5_25 * 4;
static const UINT32 WOZ1_TRACK_SIZE = 6656; // 0x1A00
static const UINT32 WOZ1_TRK_OFFSET = 6646;
static const UINT32 EMPTY_TRACK_SIZE = 6400;
static const UINT32 EMPTY_TRACK_SIZE = 6400; // $C.5 blocks
static const UINT32 BLOCK_SIZE = 512;
static const BYTE TMAP_TRACK_EMPTY = 0xFF;
static const UINT16 TRK_DEFAULT_BLOCK_COUNT_5_25 = 13; // $D is default for TRKv2.blockCount
struct WOZChunkHdr
{
UINT32 id;
UINT32 size;
};
struct Tmap
{
BYTE tmap[MAX_QUARTER_TRACKS_5_25];
};
struct TRKv1
{
@ -239,17 +264,22 @@ public:
UINT32 bitCount;
};
struct Trks
{
TRKv2 trks[MAX_QUARTER_TRACKS_5_25];
BYTE bits[0]; // bits[] starts at offset 3 x BLOCK_SIZE = 1536
};
private:
static const UINT32 INFO_CHUNK_ID = 'OFNI'; // 'INFO'
static const UINT32 TMAP_CHUNK_ID = 'PAMT'; // 'TMAP'
static const UINT32 TRKS_CHUNK_ID = 'SKRT'; // 'TRKS'
static const UINT32 WRIT_CHUNK_ID = 'TIRW'; // 'WRIT' - WOZv2
static const UINT32 META_CHUNK_ID = 'ATEM'; // 'META'
static const UINT32 INFO_CHUNK_SIZE = 60; // Fixed size for both WOZv1 & WOZv2
struct InfoChunk
{
UINT32 id;
UINT32 size;
BYTE version;
BYTE diskType;
BYTE writeProtected; // 1 = Floppy is write protected
@ -281,9 +311,41 @@ private:
static const BYTE optimalBitTiming5_25 = 32;
};
InfoChunkv2* m_pInfo;
InfoChunkv2* m_pInfo; // NB. image-specific - only valid during Detect(), which calls InvalidateInfo() when done
//
struct WOZEmptyImage525 // 5.25"
{
WOZHeader hdr;
WOZChunkHdr infoHdr;
InfoChunkv2 info;
BYTE infoPadding[INFO_CHUNK_SIZE-sizeof(InfoChunkv2)];
WOZChunkHdr tmapHdr;
Tmap tmap;
WOZChunkHdr trksHdr;
Trks trks;
};
struct WOZv1EmptyImage525 // 5.25"
{
WOZHeader hdr;
WOZChunkHdr infoHdr;
InfoChunk info;
BYTE infoPadding[INFO_CHUNK_SIZE-sizeof(InfoChunk)];
WOZChunkHdr tmapHdr;
Tmap tmap;
WOZChunkHdr trksHdr;
};
};
#pragma warning(pop)
#pragma pack(pop)
//-------------------------------------
@ -304,7 +366,8 @@ public:
}
ImageError_e Open(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary, std::string& strFilenameInZip);
void Close(ImageInfo* pImageInfo, const bool bDeleteFile);
void Close(ImageInfo* pImageInfo);
bool WOZUpdateInfo(ImageInfo* pImageInfo, DWORD& dwOffset);
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, ImageInfo* pImageInfo) = 0;
virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize) = 0;
@ -317,7 +380,7 @@ protected:
ImageError_e CheckNormalFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary);
void GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize);
void GetCharLowerExt2(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize);
void SetImageInfo(ImageInfo* pImageInfo, FileType_e eFileGZip, DWORD dwOffset, CImageBase* pImageType, DWORD dwSize);
void SetImageInfo(ImageInfo* pImageInfo, FileType_e fileType, DWORD dwOffset, CImageBase* pImageType, DWORD dwSize);
UINT GetNumImages(void) { return m_vecImageTypes.size(); };
CImageBase* GetImage(UINT uIndex) { _ASSERT(uIndex<GetNumImages()); return m_vecImageTypes[uIndex]; }

View File

@ -10,6 +10,8 @@
#define LOG_DISK_NIBBLES_WRITE 1
#define LOG_DISK_NIBBLES_WRITE_TRACK_GAPS 1 // Gap1, Gap2 & Gap3 info when writing a track
#define LOG_DISK_NIBBLES_USE_RUNTIME_VAR 1
#define LOG_DISK_WOZ_LOADWRITE 1
#define LOG_DISK_WOZ_SHIFTWRITE 1
// __VA_ARGS__ not supported on MSVC++ .NET 7.x
#if (LOG_DISK_ENABLED)