fdrawcmd.sys API

Introduction

Using the driver from your own programs requires four Win32 functions:

Getting Started

There are no other libraries or modules to link with, just a header file containing IOCTL code definitions and related structures.

Download fdrawcmd.h

Opening the device

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.

Sending requests

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.

Closing the device

Simply use CloseHandle to close the device when you’re done:

  CloseHandle(h);

Examples

To simplify the example code, no error checking is performed. Please add it to your own code!

Reading the driver version

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.

Reading a sector

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);

Determining track layout

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);

Demo Utility

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.