Using the driver from your own programs requires four Win32 functions:
There are no other libraries or modules to link with, just a header file containing IOCTL code definitions and related structures.
Floppy drives A: and B: (if available) are exposed as \\.\fdraw0
and \\.\fdraw1
, respectively.
To open the first floppy device use:
const char szDev[] = "\\\\.\\fdraw0";
HANDLE h = CreateFile(szDev, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
For asynchronous/non-blocking access to the device, you can supply a Win32 OVERLAPPED structure for the final parameter to CreateFile. If opened in this way, the overlapped structure must be passed with each request.
Device requests are made using DeviceIoControl, which takes input and output buffer parameters, and returns a status and (if successful) a count of output bytes returned.
BOOL f = DeviceIoControl(h, IOCTL_FDCMD_SEEK, &sp, sizeof(sp), NULL, 0, &dwRet, NULL);
If the call fails, use GetLastError to determine the reason for the failure. FDC error conditions are counted as failures, with most mapped to a suitable Win32 status (ERROR_CRC
for data CRC errors, etc). If you’d prefer to decode it yourself, the raw controller result bytes can still be read using IOCTL_FD_GET_RESULT.
See the IOCTL list for details of what to pass as input and output buffers. Pass NULL and size 0 for unused input/output buffers.
For efficiency, the read/write data buffers are passed directly to the driver, requiring them to be correctly aligned on system page boundaries. To ensure this condition is met, use VirtualAlloc to allocate the main data buffers.
Simply use CloseHandle to close the device when you’re done:
CloseHandle(h);
To simplify the example code, no error checking is performed. Please add it to your own code!
In addition to the \\.\fdrawX
devices, there is also a \\.\fdrawcmd
device. This represents the driver as a whole, allowing the driver version to be queried without opening a specific device.
DWORD dwRead, dwVersion = 0;
// Open the driver device object
HANDLE h = CreateFile("\\\\.\\fdrawcmd", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
// Query driver version
DeviceIoControl(h, IOCTL_FDRAWCMD_GET_VERSION, NULL, 0, &dwVersion, sizeof(dwVersion), &dwRead, NULL);
CloseHandle(h);
// Check driver compatibility
if (HIWORD(dwVersion) != HIWORD(FDRAWCMD_VERSION))
// incompatible!
The CreateFile call will fail if fdrawcmd.sys is not installed, or if no floppy drives are present (since the driver will not be loaded).
If the DeviceIoControl call is successful, the driver version is returned in the output buffer. The 32-bit value holds the dotted components of the version number, so 0x0100010B
is version 1.0.1.11.
You should always check the driver version before using it. If the upper 16-bits differ from the compile-time version number (FDRAWCMD_VERSION), the driver incompatible. You can either recommend a driver update (if it was older), or that they get a newer version of your program.
The following code reads a single 512-byte sector 1 from cyl 0 head 0:
DWORD dwRet;
FD_READ_WRITE_PARAMS rwp;
FD_SEEK_PARAMS sp;
BYTE datarate;
// Details of sector to read
rwp.flags = FD_OPTION_MFM;
rwp.phead = 0;
rwp.cyl = 0;
rwp.head = 0;
rwp.sector = 1;
rwp.size = 2;
rwp.eot = 1+1;
rwp.gap = 0x0a;
rwp.datalen = 0xff;
// Details of seek location
sp.cyl = 0;
sp.head = 0;
// Data rate (250Kbps for double-density, 500Kbps for high-density)
datarate = FD_RATE_250K;
// Data buffers must be correctly aligned for DMA use
PVOID pv = VirtualAlloc(NULL, 512, MEM_COMMIT, PAGE_READWRITE);
// Open A:
HANDLE h = CreateFile("\\\\.\\fdraw0", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
// Seek to cyl 0
DeviceIoControl(h, IOCTL_FDCMD_SEEK, &sp, sizeof(sp), NULL, 0, &dwRet, NULL);
// Set data rate to double-density
DeviceIoControl(h, IOCTL_FD_SET_DATA_RATE, &datarate, sizeof(datarate), NULL, 0, &dwRet, NULL);
// Read the boot sector
DeviceIoControl(h, IOCTL_FDCMD_READ_DATA, &rwp, sizeof(rwp), pv, 512, &dwRet, NULL);
// Cleanup
CloseHandle(h);
VirtualFree(pv, 0, MEM_RELEASE);
The following code determines the MFM sectors present under head 0 on cylinder 0:
// Allow space for 32 headers
BYTE buf[1 + 32 * sizeof(FD_SCAN_RESULT)];
PFD_SCAN_RESULT psr = (PFD_SCAN_RESULT)buf;
// Open A:
HANDLE h = CreateFile("\\\\.\\fdraw0", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
// Set up scan parameters
FD_SCAN_PARAMS sp;
sp.flags = FD_OPTION_MFM;
sp.cyl = 0;
sp.head = 0;
// Seek and scan track
DWORD dwRet;
DeviceIoControl(h, IOCTL_FD_SCAN_TRACK, &sp, sizeof(sp), psr, sizeof(buf), &dwRet, NULL);
// Process the returned header list
for (int i = 0 ; i < psr->count ; i++)
{
// Do something with psr->Header[i]
}
CloseHandle(h);
DiskUtil is a sample utility to read/write/verify/format most regular format disks. The default geometry setup is for the SAM Coupé, but you can easily change the values to fit other formats.
The archive below contains a Visual C++ 6 project and the source code, as well as a pre-built EXE for your convenience. You can use as much of the code as you wish in your own programs.
DiskUtil.zip (8K)
Questions? Please get in touch using the link below.