I believe I've already provided some details on CrossDos, but nevermind doing it again and going a bit more into depth. CrossDos is also pretty much new, at least many major parts have been exchanged, and there is unfortunately still one bug left we need to fix in a follow-up release (tentatively, 3.1.4.1). The big picture is that CrossDos is now multithreaded, same as the FFS, and it can handle large volumes (though, due to the bug, not long FATs). That means, it supports FAT16,FAT24 and (that is the new part) FAT32. It also includes support for long file names, something called "VFAT" in the Linux world. Last M$ patents on this stuff run out Febrary last year, so we are safe.
Multithreaded means that CrossDos can fire off an I/O operation such as reading a long file, it can still respond to other incoming requests. As long as the requests can be handled from cache - and CrossDos does have a cache - they can be answered immediately. Previous CrossDos releases tried this in a clumpsy way, by always starting two processes. One handled "ACTION_DISK_INFO", and forwarded all other requests to a second task. This would at least avoid slowing down the frequent disk check by the workbench which triggers exactly this packet, but doesn't help much otherwise. New CrossDos is a single process, not two, but it uses a technique called "coroutines", something it shares with FFS and the CD File system (more on this in another episode).
For normal routines, you have subroutines A, B, C where A calls B, then B calls C, C returns to B, and B returns to A. Coroutines work different. From A's perspective, A calls B, and - from inside B - B calls A. A typical example for this (and this is how it works in just another project of mine, ViNCEd) is parsing of CSI sequences, e.g. CSIx;ym to set the font color and font attributes in the console. Here we have one function B that calls A to get a new character, then, if this is a CSI or ESC, it reads the next - ok, is it a number? - parses the number, then continues parsing to the next semicolon, then when the final letter arrives, it has all the parameters it needs to collect, and calls the corresponding lower-level function to set the attributes. So, the parser - here B - needs the data letter by letter or digit by digit. Unfortunately, this is not the way how the data arrives. Instead, the user writes data into the console, and probably not even in complete sequences. There is nothing wrong writing the CSI with a single write call, then writing the digits in a second call, and then writing the final letter in a last call. So, from that perspective, the function that receives all the request from the user - and this is A - gets data in chunks, and then calls into B. So from the perspective of the dos.library, the top-level is A, which collects user requests, and calls into the parser B. From the parser perspective, B namely, it needs to call A to retrieve the next character. Clearly, this doesn't work with classical subroutines, because either A or B would need to be turned "inside out".
There are two solutions for this problem. One solution is "state machines", which are hard to debug and complicated to write, but classical and work with the methods of high-level languages such as C, and the other solution is "coroutines", which is a long forgotten art form, probably best described in Knuth's "The Art of Computer Programming", which is not supported by C, but requires a small assembly stub. It is (or was) supported by BCPL, as implemented in Tripos, and probably one of the reasons why the OFS used it. FFS uses its own coroutine functions, and so does CrossDos and CDFileSystem.
There is more to tell with CrossDos. My experience with it is that most people assume that it works like FAT95, but it does not. FAT95 is not multithreaded, and it is not quite as capable as CrossDos. Again, we need to take a detour for the details. On the Amiga, partitions are indicated by the Rigid Disk Block, a couple of blocks at the start of a disk that tells where the partitions start and end. This rigid disk block is to be interpreted by the firmware of the host adapter. This is for example the Guru ROM, or the GVP boot rom, or even part of the scsi.device. It is not the FFS or OFS that deals with it. Floppies don't use a Rigid disk block, and removable media such as ZIP should not use it either (this is a bad idea, more later).
Of course, PCs don't use a rigid disk block. They use a Bios MBR partiton table (not nowadays anymore with UEFI, they use yet another structure). The problem is: The hostadapter cannot read this structure (there is not even a hostadapter there), so nobody in the Os feels responsible for the MBR partition table. So CrossDos has to jump in. It can be run in two modes:
- Using the drive entirely as if it would be a floppy (this is the "superfloppy" mode)
- Interpreting the partition table, then handling one partition of the drive. It then needs to know which partition it should be responsible for.
Back when CrossDos became implemented, the new "SuperFloppy" flag in the mount list did not exist, so they looked for other solutions, which is the DosType field in the mount list.
So, if the dos type is MSD\0, it is a superfloppy, no partitions are assumed, and the whole disk is just one file system. This is identical to setting "SuperFloppy = 1" in the mount list. There is a second equivialent dos type, namely MDD\0 which does right the same. This is probably some legacy mechanism to switch between single and double density, but it is no longer in use and the two are now equivalent. One single file system per device.
If the DosType is MSH\0, instead a MBR partition table is assumed to be on the drive, and then "SuperFloppy" must be 0, i.e. off. Of course, then CrossDos needs to know which partition to access, and there is no such field in the mount list. Instead, it uses the name of the handler. If the device name ends with C, it is the first partition, if it ends with D, it is the second partition and so on. This is probably to mirror the typical Dos "partition names" where C: is the first partition on the internal hard disk, D: the second and so on. Hence, you cannot name the devices as you like, but the name of the icon in DEVS:DOSDrivers nees to end with the right character names.
Thus, to keep it short, three things need to be fixed to switch over from FAT95 to CrossDos:
a) Change the FileSystem entry in the MountList to L:CrossDosFileSystem
b) Change the dos type to 0x4d534800 for MBR-partitioned drives or 0x4d534400 for super floppies (or keep 0x4d534800 and add "SuperFloppy = 1").
c) Change the name of the icon such that the last letter indicates the right partition.
FAT95 uses a different dos type, namely 0x46415400, and uses a different way to indicate partitions, and people often forget to change this. In particular, FAT95 uses the last digit of the Dos type to indicate the partition number, i.e. 0x46415401 is the next partition.
There is, however, something wrong with this hack, and it is a bad idea what FAT95 attempts here. And for this, we need another excursion, and this is the FileSystem.resource. This is an invention that came into live with v34 (Kick 1.3) and autobooting. The problem it tries to solve is that if you have a harddisk with many partitions and a floppy, then each of these devices needs a file system (clearly). However, without any further trick, the same file system needs to be loaded once per partition, hence for a three-partition drive and two floppies, five copies of the FFS would have to be loaded into RAM. This is clearly a very bad idea and wasteful.
Instead, we have the FileSystem.resource, which "buffers" file systems. So, whenever a partition is mounted, the Mount command (or the firmware of the host adapter) checks in this resource for a file system with the same DosType as the one it needs for mounting the partition, and if it finds one, it just recycles what it finds there. Hence, there are three CrossDos entries in the FileSystem.resource once you mount a crossdoss device, installed there by CrossDos itself: MSH\0, MSD\0 and MDD\0, and all three point to the same CrossDos code.
Now, consider the same for FAT95: Instead, you could have as many as 255 entries in the FileSystem resource for that: One for FAT\0, the first partition, one for FAT\1, the second partition and so on, up to FAT\255, the 256th partition. This is clearly not practical. FAT95 mis-uses the DosType for something it was not designed for, and its authors probably forgot about the implications this has for the FileSystem.resource.
In the meantime, CrossDos had one bug fixed, namely that the version that came with 3.1.4 accepted only FATs (directories) with at most 16 blocks in size. This was part of a legacy check mechanism to identify corrupt disks, but is no longer relevant for today, so it got removed. CrossDos now *also* supports the FAT\xx dos type just because I know that only a minority of users read instructions, or even this text, but there is no entry (for reasons given above) for this DosType in the FileSystem.resource, so the code isn't shared. It just loads CrossDos again from disk, wasting your memory.
Avoid the waste, change the DosType.