Floppy emulation
This rather technical post describes how floppy drive emulation has been implemented in zeST.
I will try to explain about some internals of the ST, and the technical choices I made to implement floppy functionality that would work as similarly as possible to the real hardware.
The ST architecture around floppy disk management
Here is an overview of the different main components of the Atari ST that are responsible for managing the floppy drive:
In a nutshell, how this works:
- The 68000 processor issues commands (motor on/off, read, write, change track etc) to the WD1772 controller chip via a dedicated interface of the DMA chip
- The WD1772 executes the commands. If the commands are read or write, the DMA chip is responsible for transferring the data from/to the main memory using the bus request mechanism of the 68000.
- The YM2149 (yes, the sound chip!) is used by the 68000 to select the side on double sided disks. Indeed, the YM2149, besides managing sound, also features two 8-bit I/O ports that are actually used on the ST. One is used for the parallel port output, and the other manages various flags, among which floppy drive activation, floppy drive selection (when using two drives) and side selection. Fun story, early versions of zeST only managed single-sided floppies, because the YM2149 had not been implemented yet.
This is voluntarily simplified because other chips are involved, too, like the GLUE that actually performs the bus requests in place of the DMA, or the MMU that manages the memory address pointer to determine where to read or write the next bytes of data in the memory.
The floppy drive
The original floppy drive is quite a simple device : It mostly consists of motors to spin the disk and step by step move a read/write head on the different tracks of the disk.
The signals exchanged through its connector are very basic: they are binary (0 or 1) commands to activate or deactivate the drive, to activate or deactivate the motor, to enable or disable writing, to detect the beginning of a track, to move the head one way inwards or outwards the disk, etc. Two pins contain the value of the (analog!) signal read and to be written as the disk rotates.
The disk rotates at 300 rotations per minute, which is 5 rotations per second, or 1 rotation every 200 ms (milliseconds). In the ST’s double density format, one bit is transferred every 4 μs (microseconds), so that means 50000 bits, or 6250 bytes of (unformatted) data are theoretically recorded on each floppy track. I said “theoretically”, because the rotation speed is not exactly 300 RPM, it can be a little bit more or less than that. Moreover, the rotation speed may even not be constant, and can oscillate between a minimum and a maximum, provided it remains within tolerancy bounds. For this reason, the number of bytes on a track may not be exactly 6250, but a few bytes more or less, and can vary from one track to another.
This property can be used by some disk copy protection mechanisms, where the disks are written using a slower rotation speed, allowing more bytes on each track, while remaining within the tolerancy bounds allowed by the floppy disk controller. The copy protection works because this kind of track formatting can not be written again by the ST’s drive itself.
In order for the data stream to be correctly recognized by the floppy disk controller chip even though with a variable bitrate, the stream comprises a clock signal interlaced with the data. This way, the controller can isolate the different bits with a limited error rate.
zeST’s floppy drive
Of course in zeST, there is no actual physical floppy drive, but I really wanted to simulate it as closely as possible, within normal operating parameters. Still, I did not find any real interest in emulating variable rotation speed. I chose to use a fixed speed of exactly 5 rotations per second, allowing exactly 6250 bytes per track. Some protections will not work in this setup, I’m aware of that. I’m not planning to work on that part in the near future, but since zeST is a free/open source project, feel free to propose any improvements.
In zeST, the floppy drive is simulated in two different worlds: hardware and software. The hardware part, implemented on the FPGA, is responsible for simulating the rotation of the disk, sending/receiving data bits to/from the WD1772 controller at the real effective bitrate of one bit every 4 μs. The software part, which runs on the ARM processor embedded in the Zynq chip, is responsible for providing the actual data from a floppy image file to the hardware part, and vice versa. The communication between the two is done through hardware registers, implemented on the FPGA that are made available to the CPU as memory mapped variables. An interrupt mechanism informs the CPU that the FPGA has done reading the data interchange register, and that another value must be sent. Initially, that register was 32-bit wide, and as the data is sent at 4 μs per bit, a new value had to be provided every 128 μs. However, this rate of 128 μs happened to be a bit too fast for the CPU that could now and then miss some interrupts and fail to transfer the data correctly. This was solved recently by using a 128-bit wide register instead, reducing the rate of interrupts to one every 512 μs, with zero misses now. So we can say that the emulated floppy drive is now working flawlessly, at the same speed as the real hardware.
The WD1772 controller
The WD1772 is the floppy drive controller. It is responsible for:
- Converting the 1-bit data signals (in and out) to/from well-constructed 8-bit bytes. When reading from the floppy, it uses the interalced clock signal and the data in the gaps between sectors to resynchronize its internal clock with the data clock, in order not to miss bit values.
- Executing commands from the main processor, like select track, read sector, write sector, format track, etc. When reading or writing data, the data bytes come in and out from a 8-bit I/O port. On the ST, that I/O port is connected to the DMA controller.
In zeST, I do not strictly manage data bit clocks and variable disk rotation speeds, so the 1-bit to 8-bit conversion is drastically simplified. The data comes directly as a synchronous digital signal, so no need for analog to digital conversion and clock detection.
However in the commands management part, I tried to be as close to the hardware as possible. The original WD1772 data sheet, that can easily be found on the web, was very helpful in understanding the different state machines and behaviours involved, even if there were quite a share of bugs ! VHDL simulation tools have been very helpful to test use case scenarios and validate the design.
About data formatting, my implementation of WD1772 is using the low level formatting of the real hardware, so this is why the native floppy image format I’m using (the MFM file format) contains the raw contents of each tracks, with absolutely no header information or anything. The track raw format contains, besides the contents of the data sectors themselves:
- Many bytes that work as gaps between sectors, allowing the floppy drive to resynchronize its internal clock with that of the data signal,
- Bytes that define sector headers with useful metadata for the disk controller (sector, side and track number, CRC bytes, sector size etc).
- Padding bytes at the beginning and end of the track.
The contents is the same as a real floppy disk. Just like on the real hardware, the operating system must read the boot sector to understand the formatting of the disk. This means you can use formatting tools to format floppies in any particular way, with sectors of different sizes, etc. The limits are the same as the real hardware. You can create a blank unformatted MFM file as simply as creating an empty file of 0 bytes with the “mfm” file name extension.
For those interested, here is a very detailed page about floppy tracks formatting.
I have only recently added support for the more standard .ST image file format, which is well known by emulator users, so this will cause low trouble using your files in zeST. Those image files are actually internally converted to standard formatted MFM tracks, and converted back to just the sectors contents before being written back to file when writes have been performed to disk.
The DMA controller
The DMA controller is responsible for transferring data between the main memory, and the floppy/hard disk controllers. This releases the 68000 processor from managing data transfers, byte per byte, all by itself. The DMA chip embeds two buffers of 16 bytes, that are used for packetising the transferred data to/from the main memory, taking profit from the bus arbitration mechanism offered by the 68000. In such a setup, the DMA informs the GLUE chip that it has valid data to transfer. The GLUE asks the 68000 to let it use the bus, so the 68000 actually goes to a pause mode, then the DMA is allowed to send the data to the bus or read from it. The MMU chip, at the other end of the bus, knows where to put data or read it from, thanks to its internal DMA address pointer. This bus arbitration mechanism is the same that is used for the Blitter.
This double buffering system allows to simultaneously perform bus transfers and data interchange with the disk controllers.
In zeST, the implementation of DMA has been rather straightforward. Its functionality is rather simple, so rewriting it in VHDL was not too big challenge. For now, zeST’s implementation of DMA only works with the WD1772 floppy controller. Hard disk management has not been implemented yet, but it should come quite soon in zeST.
Conclusion
I have presented how floppy disk management works on the ST, and how I implemented it in zeST. I believe it is very close to how actual floppy disks and drives work, and you should have a very similar experience in zeST, compared to the original hardware.
If you have comments, suggestions, feel free to ask by replying to the dedicated twitter post.