It took me two years of my life but I learned more than I ever would have doing something else. It's good to have supportive people around you when you attempt something like this, and it also helps to have an old guitar push button wired up to your reset switch until you get it booting properly. You'll be resetting your machine lots of times.
I used the minix filesystem because it was very simple and I could fit the code to load the configuration and boot files in to a small boot loader.
I used an old 16 bit compiler for that part, PCs do not generally boot up in 'protected mode', so your first stage is spent on moving stuff from one side of the line to the other, then finally make the switch and continue from there.
The stages, iirc were:
- master boot loader
- partition boot loader
- file system boot loader (16 bit)
- system tasks (fs, idle, task (the scheduler) and a console driver running a shell)
Because the OS used message passing it was relatively easy to build and test the bits outside of the working os, after all as long as the messages were having the desired side-effects it was all good.I used borland turboc for the 16 bit file system aware boot loader, and DJGPP for the boot strapping to a self-contained os. As soon as that worked and I was able to get GCC running on the other side I focused on recompiling everything from scratch until the binaries matched exactly with the ones I had been making in cross development mode, after that things went a lot faster.
That first stage took several months, I don't remember cursing so much in my whole life.
I wouldn't trade that time for anything though, it was better than any other programming experience before or after.
The OS I wrote was a functional equivalent of QnX, at the time only available for the 286, and I very much wanted a 386 version.
Several others had a hand in there, Marcel Wouters completely re-wrote the console driver, and a large part of the window manager was based on a list package that was written by another friend.
For networking I used the ne2000 driver from the freebsd project with a bunch of tweaks to get it to work as a user process.
The hardest part of the whole thing was to get a hard-realtime response on interrupts in user processes.