OK, I think we're in business! I'm going to switch to the ATMEGA1284 to gain some additional RAM buffering space (though not as much as a 1MB external RAM to buffer the whole disk). That, plus some emulator code improvements, should be enough to support most types of writes on most SD cards, I think. The biggest challenge is still emulating initialization of a floppy disk when using a lower-speed SD card. During floppy initialization, the Mac continuously writes both sides of every track without stopping, so the write rate is the highest of any kind of write operation. Today I thought of a couple of ideas that I think will help with initialization emulation, and other cases where an entire side is written in one pass.
The first idea is to special case the handling of empty sectors. During floppy initialization, the Mac writes 1600 sectors very fast. What’s in those sectors? Zeroes. Instead of buffering a 512 byte sector full of zeroes, I could just set a flag that says “this sector is all zeroes”. Using a bitfield, I could buffer an entire disk’s worth of zero sectors using just 200 bytes of RAM. Those sectors could then be saved to the SD card whenever it was convenient, after the floppy initialization was finished. If a read request arrived before all those zero sectors were saved to the card, the emulator could check the flag first to see if an all-zero sector should be synthesized instead of actually loading the sector data from the SD card. This solution is short and simple, though its usefulness is limited to floppy initialization only.
The second idea is to intentionally create an error condition to slow down incoming data, by exploiting some code that measures the size of the gap between the last sector and the first sector on one side of a track. During initialization of a floppy, after the Mac finishes writing the last sector on a side, it immediately switches back to read mode to measure the gap before the next sector, and confirm that the next sector is sector 0.
From examining the ROM disassembly, I discovered that the disk initialization code uses some kind of progress counter that starts with a value of 7. Every successful side written increments the counter by 1. If the gap is the wrong size, the counter is decremented by 1. If the counter value is still greater than 4, it attempts to rewrite the side again, otherwise it aborts with an error.
By intentionally generating a bad gap size after a full side is written, I can force the side to be rewritten. If I also make the emulator smart enough to detect when data written to a sector is identical to what was already there, then it can ignore the second rewrite. That effectively doubles the amount of time available for saving the track data to the SD card, since every side will be written twice by the Mac.
The bad gap size trick can only be done once per side, or else the progress counter will decrease and the initialization process will eventually fail, so it can’t buy an indefinite amount of additional time. It’s also a little risky, because it means the progress counter will never increase above 7, and any 3 other errors occurring during the initialization will cause it to fail.
I did some simple tests of this idea that look promising. By disabling SD saves, I was able to perform floppy initialization to measure its write speed, even though the initialization ultimately failed during the verify phase. In my initial test, it took 34 seconds to complete the write phase when initializing a floppy. After I added emulator code to generate a bad gap after every other side write operation, the time increased to 59 seconds, with no obvious ill effects.