BigDumbDinosaur wrote:
floobydust wrote:
So, time for yet more testing... where the bulk of it will be done next week once I'm back to my normal location. Overall I think this is a good update for DOS/65. I'm still thinking about delayed writes of changed LBAs to the disk to improve performance, but that will require some changes to the BIOS and it's interrupt structure, hence a later update.
Implementing a buffer pool shouldn't be too difficult, other than finding sufficient RAM in which to place it and a table to manage the pool.
If it were me doing it, I’d buffer disk blocks, not individual CP/M records. If records are buffered, then when one becomes “dirty,” two disk accesses are required to flush the record, as it would be necessary to fetch the block in which the dirty record belongs, re-write the record and then re-write the block. On the other hand, if blocks are buffered and a record in a block that is already buffered is re-written, you only need one disk access to re-write the block.
Either way, the interesting thing will be in working out how to periodically flush dirty buffers. Since CP/M is a uni-tasking environment, you don't have the luxury of having a process automatically run at periodic intervals to sync your buffers as they age.
One method would be to have some code (“buffer flusher”) that is executed when the PEM gets ready to return to a calling program. That code would scan the buffer pool table looking for dirty buffers that have aged out. When one is found, it would be written and marked clean. Other than the time required to write buffers, this process should have little effect on performance if the buffer pool table is properly organized and buffer age is stored in a binary format (not normal time-of-day format, which is too computationally-expensive).
In order to avoid having the buffer flusher run on every return from the PEM, a timer field somewhere in RAM should be decremented by your IRQ handler at one second intervals, and when it reaches zero, it would be reset and a flag would be set indicating it’s time to flush buffers. The timer would remain stopped until the flush-the-buffers flag has been cleared in the foreground.
Meanwhile, on each exit from the PEM, the flush-the-buffers flag would be checked and if set, the buffer-flusher code would be executed. Once that is done, the flag would be cleared. The IRQ handler, which would be polling the flag at periodic intervals, would see that it has been cleared and would restart timer.
There would also need to be a command to tell DOS/65 to immediately flush all buffers. Obviously, doing so would be necessary before powering down the system. All good points.... so far, the SIM code basically keeps a 512-byte LBA in a default buffer in low RAM. The SIM routine for reading transfers some pointers around and strips off the lower 2-bits of the lower order byte, which is the PEM Record offset (128-bytes) to be read and moved to memory at a location that PEM provides thru a separate call. Once the Read routine saves the 2-bit record offset, it calls a routine that calculates the LBA from the PEM Record and active drive number, then checks to see if it's in the LBA buffer. If not, it loads it and returns back to the SIM Read routine, which only needs to move one of 4 records from the buffer to memory. Then again, if the LBA that holds the requested PEM Record is already loaded, the routine just returns to the SIM Read routine. Needless to say, this is what I call a "cheap cache" as each LBA read has 4 PEM Records. This approach works fine... and once I got the code right, the read performance was good overall.
The problem from a performance view is writing the PEM Records. Right now, I'm using the same core routine to ensure the correct LBA is loaded into the buffer, then the SIM Write routine moves the updated Record into one of the 4 Record offsets in the LBA. Once this is done, I write the LBA out to disk. This becomes the bottleneck. I did some simple testing attempting to get a similar approach to reading records, but alas, it hasn't worked out... managing a dirty block flag to ensure an updated LBA is written first, it has some odd issues, meaning things aren't getting written out properly, or at all.
There is a chance that the onboard write-cache on the Microdrive (other system) might be more useful in enhancing the overall performance, but my end goal is still to get a single LBA buffered for record writes and hopefully improve the performance, especially for writing larger files, such as a file COPY program. Still a ways to go on this one... but tomorrow is a travel day... so timing is everything!
Oddly, the default blocking/deblocking and track/sector code performs better on write performance, so I need to dig thru that yet again... as it does have some flag bits depending on the type of write it's doing. It appears that it is managing to hold some record writes before writing the LBA to disk.