Before I could write a program to install device drivers, I first had to work out how it was done. My main problem was that I had no knowledge of the internal DOS tables into which I had to enter all the drive statistics. I was aware that the procedure for loading a device driver went as follows:
Steps 1-5 are simple operations, the information and procedures
necessary being available in any semi-decent technical reference for
Step 6 involves the use of an undocumented DOS function, function 52h. This gets a pointer to a DOS table 'invar'. Each device driver started with a DWord pointer to the next device driver in the chain. The first device in the chain is the NUL device, which is located at invar + 0022h. To link the new driver into the chain, the NUL device must be changed to point to the new device, and the new device must be made to point to where NUL used to point to. This installs the driver in the same place in the chain as if it had been installed in CONFIG.SYS. At this point, I had no idea about how to implement step 7, so I concentrated on getting the rest to work. This was enough to install character devices.
I started by doing it all by hand in SYMDEB, and once I was sure of the procedure, I wrote the first version of DEVLOAD. As can be seen from the alteration list, it was very primitive. I soon made many changes to improve the functionality and user-friendliness of the program, all of which are listed in the source code.
At this point, I wrote SHOWPARM.SYS to display exactly the command line passed to the driver by DOS. This showed that when no parameters are given to a device driver, DOS inserts a space after the filename. DOS also converts the whole of the command line to upper case. For compatibility, I made DEVLOAD do the same. in version 2.1.
Once I had cleaned up the program to a reasonable extent, I decided that it was time to include block devices.
At this point , I was stuck. I had no idea of how to continue, so I was forced resort to the age-old principle 'If you don't know what you're doing, copy someone else.' I realised that there was already a section of code in the system that installed blocks into the internal tables. It is part of the SYSINIT module, the section of code which sets up DOS immediately after booting and is responsible, among other things, for the parsing of CONFIG.SYS and the loading of device drivers.
I wrote a device driver, calling it WOMBATS.SYS in the absence of any better ideas, which grabbed the entire SYSINIT module from it's location in high memory into a buffer and reported the return address (the address which the driver was loaded from.)
This allowed me to examine the SYSINIT code at my own leisure. After a couple of days inspecting the SYSINIT code, I managed to complete my list of what step 7 in my original flowchart actually entailed:
The first four steps were easy enough to implement, but step five was partially done by another undocumented DOS function, number 53h. This is used to expand the BPB returned by the device driver into the block header. Step 7.5 was as follows:
I now had a complete flowchart, at least at a greatly simplified level,
for the installation of device drivers. I proceeded to update DEVLOAD to
version 2.0, including block device installation. Again, it was
primitive to start with, but I soon updated the program as and when I
realised what could be done to improve it.
Because the program started off as a test for an idea and developed from there, there was no flowchart and program plan, but it evolved as it went. When I started to write it, I had no idea of the problems I would encounter, so attempting to write a program plan and flowchart would have been totally pointless.
As it was, the program stayed reasonably well-structured, in spite of the continual alterations. Even so, I decided to write out a flowchart, including a few new ideas, and do a complete rewrite, putting it in .EXE format and calling it version 3.0.
This was fine in theory, but almost as soon as I had finished the rewrite, I finally managed to work out how to relocate the PSP and save the 60h bytes of memory below the driver that I had always before had to leave for the PSP. Having done this at last, after about a year, on and off, of experimenting, I made many changes to the memory allocation procedure, as can be seen in the alteration list for version 3.0.
Having never found a device driver file containing two drivers with blocks to install, I had a few bugs in the install procedure for block devices which had never shown up. I put together two copies of DRIVER.SYS into a single file, and this highlighted a few problems which I also fixed.
The only other change which I made after writing version 3.0 was to move the main program entry point to a location above LASTBYTE. This results in absolutely no change to the program code, it just means that the code which has already been executed and is no longer required is not relocated to the top of memory.
The final flowchart for DEVLOAD.EXE is included after the appendices.