Hi,
Colonel Kernel wrote:I guess what I'm trying to say is that writing reusable asm requires far more self-discipline. Any development process requiring that much self-discipline is inherently not scalable.
IMHO you can deliberately write reuseable, or you can deliberately write highly specialised code. Sometimes these are mutually exclusive and sometimes they aren't.
I always try to write highly specialised code. Sometimes I can cut & paste older code, but in general I can't. Usually (for me) there's larger changes, and it's these larger changes that cause the rewriting.
For an example, let me compare my last code to the current code - they're both mostly similar (modular) micro-kernel designs and the only main difference is that the newest code is designed to support headless systems and diskless systems. I'll focus on all the boot code because neither of them got much further than that (so far).
First, there's the boot loaders. The 1440 KB floppy boot loader started as the old code, but I added code to make the PC speaker beep when there's an error (for headless systems) and some code to detect if a VGA card is present (to automatically force a "headless boot" if there's no VGA). The Bochs ROM boot loader and the PXE/network boot loader are both entirely new code (nothing similar in the previous version).
Next is the "Boot Manager". About 80% of it is re-used code while the other 20% is was rewritten due to different physical memory usage and different keyboard/video methods (it used to directly access keyboard and video, but this was split into a seperate module).
There's also a "fake" boot manager that decompresses boot images, and a utility to compress the boot images (written in portable C code). The algorithm I was using to compress the boot images was too slow (especially for large boot images), so I rewrote this utility to use a different algorithm (now it uses more RAM, but compresses much faster). When I rewrote the compression utility I didn't care about making the output compatible with the old version, so when in the end I rewrote the OSs decompression module too.
Then there's the boot user interface code. There's 2 versions of this - one for VT100 over the serial port (for headless systems) and the other for keyboard/VGA. The VT100 code is entirely new. The keyboard/VGA code was mostly rewritten because I changed the kernel log format (stripped out the colour control codes and replaced them with bold and highlight codes, because VT100 can't reliably handle colour) and improved the way it works for better performance (instead of scrolling each time the cursor reaches the bottom of the screen, it pre-checks the new text and does one big scroll, then draws the new text without caring about hitting the bottom of the screen - end result is a lot less time spent scrolling when several lines are added at once).
The physical memory manager boot module is used to detect the system memory map and dynamically allocate pages before the kernel is started. The last version took into account page colour and NUMA domain, so that any code that allocates a page got the best possible physical page for it's purposes. Because of the complexity of it I had an "allocate a page" function in the physical memory manager that was added to the boot API, and the physical memory manager had to remain in memory (like a TSR, if anyone remembers DOS programming). I decided this wasn't such a good idea - the boot code was "32-bit only" and couldn't handle pages above 4 GB, it tended to impose it's data structures on the kernel's own physical memory manager (which was meant to be easily changeable), and because it was the only boot module that needed to stay in RAM after it had done it's thing it made the boot manager messier.
The new version uses a much simpler approach - one bit per page, without caring about page colours or NUMA domains. This means the physical memory manager used during boot is radically different (different function parameters, different data structures, etc), and meant changing all the code that uses the physical memory manager during boot (mainly code that sets up paging for the kernel) so it allocates it's own pages directly from the "free page bitmap" instead of using the boot API. It also means that the kernel will need to be able to optimise the physical pages it's using (something like a "kernel memory optimiser" running in the background that exchanges the physical pages the kernel is using for better physical pages).
The "CPU detection" module was mostly cut & paste, but I added code to detect "CPU forgery" and code to handle the errata for a lot more CPUs.
Then there's a module that handles NUMA domain information. This was mostly cut & paste, but the old code detected areas of the physical address space that are reserved for hot plug RAM. This got removed as it has nothing to do with NUMA domains and belongs in the physical memory manager boot module (it's unfortunate that the ACPI SRAT table contains a lot of NUMA information *and* the hot plug RAM information - the old code parsed this table once, while for the new code the same table is parsed twice).
Overall, I'd estimate about 20% of the code was reused, 40% was rewritten due to change, 10% was rewritten for no good reason, and the remaining 30% is entirely new. If I was working in C (or any other HLL) and if I had deliberately written the original code to be reusable (which I didn't), it really wouldn't have made much difference (I'd still rewrite a little bit for no good reason, that's just the way I do things).
Cheers,
Brendan