Going through my virtual attic: Ulsop
I was always fascinated about how operating systems work - managing the hardware, keeping the applications running ... So I always wanted to write my own OS since I was a kid. At first I wrote some batch files and QBasic programs to extend what COMMAND.COM from DOS could do. A couple of times I tried to write my own text based command shell in QBasic to replace COMMAND.COM - all those attempts didn't really work, I never used them myself, and they are now lost to bit rot and lost floppy disks ....
But when I knew C and a bit of assembler I tried to give it another go. It must have been when I was around 13 to 14 years old - there are no dates in the C files and file timestamps have been lost to copying the file around between different storage media. I estimated the date by comparing it to other programs of that same timeframe which have dates in them or where timestamps survived.
What I came up with, Ulsop, was again just a shell, not a real operating system. It's still based on DOS. The goal was to first create the graphical shell and then later come up with something that would implement just enough DOS to get Ulsop running. Which is also how it got it's name. "It's not a real operating system, it only acts AS IF it was one", or in German, "es ist kein echtes Betriebssystem, es tut nur so ALS OB" -> Ulsop. I think that was quite clever :)
Here are some things that Ulsop does and have:
- Its own loadable drivers (mainly video and mouse)
- Its own executable format (.UXE)
- Cooperative multitasking
- Graphical user interface with mouse support
- Toolkit for buttons, checkboxes and radio buttons
Here's what it doesn't:
- Memory mangement
- Disk management
- IPC between programs
- Have windows (never planned, all programs run fullscreen)
And here's what you can do with it if you run it:
- Roll some dice (random number generator)
- Play Snake
- Play Tic-Tac-Toe (if the mouse works)
- Switch through running programs by pressing F12
- Get angry at it because it crashes all the time and behaves weirdly
So, yeah, it's a simple shell with very few programs. But there's quite a bit of low level stuff going on and it certainly was a great learning exercise for me.
First let's go through the design of Ulsop. As I mentioned before, Ulsop is based on DOS and runs in Real Mode so you start it by running LOADER.EXE from a Windows (3.1 - XP) DOS window or just plain DOS. The first thing the loader does is setting up some interrupt handlers:
0xFF Main API entrypoint
0xFE Pointer to kernel/userspace communication space (don't call int 0xFE! It points to data)
0xFD Driver chain entry point (initialized to point to an IRET)
0xFC Function, programs can call to yield CPU time (initialized later, for now just points to IRET)
0xFB Function to switch the foreground task (like Alt+Tab. In Ulsop F12)
The int 0xFE space is used to pass function codes and parameters when calling int 0xFF. I couldn't find a way to "properly" pass data through CPU registers.
Then it reads ULSOP.INI which contains some settings like the main screen resolution and programs in autostart.
After that, all enabled drivers from the textfile DRIVERS.USS are loaded, one at a time. Each driver gets an ID based on the line number of the text file. The ID is put in the first field of the int 0xFE data structure, then the driver file is executed. The driver would typically remember its assigned ID somewhere and hook itself into int 0xFD, chaining all calls it's not responsible for to the previous handler. Drivers are simple DOS .COM or .EXE files which stay resident.
Then it looks for a display driver providing the desired display resolution, loads all fonts from FONTS.USS, looks for a mouse, switches to graphics mode and draws the taskbar. (I don't remember why it was so important to draw the taskbar at this point of the loading procedure, but it does ...)
The loader prepares the first process OSLOADER.UXE and finally runs ULMULTI.COM which starts executing userspace UXE programs. I've chosen this design because ulmulti needs to manipulate the stack pointer - which you can't do in the inline assembler of the C compiler I used. I think this design is what causes Ulsop to fail when running in DosBox, beacuse it gets stuck right after drawing the taskbar. But it "works" on MS-DOS and FreeDOS.
OSLOADER.UXE will then start all the autostart programs specified in ULSOP.INI (for now just a rudimentary desktop called DESKTOP.UXE) and terminate itself.
Then the desktop is ready and you can work and play with Ulsop! Yay!
ULMULTI.COM will terminate when there are no more tasks running. At this point the loader will switch back to text mode, deinitialize all drivers, free their memory and return to DOS.
At some point it became clear to me that there are many many design flaws in Ulsop which is why I abandoned it:
- The mouse driver needs to draw the pointer by itself, which is stupid - also prevents SVGA from working with most DOS based mouse drivers
- The lack of any memory mangement makes it tough to force quit applications
- The multitasking approach is really flawed. Basically a program has to check: Was there a mouse click? Was there a key press? -> and then yield its CPU time. So the CPU always sits at 100%, possibly doing thousands of pointless taskswitches per second with no way to HLT in the meantime
- I think there is a lot of memory corruption which happens silently and then just brings everything down randomly. No idea where to start ...
- I tried to port a C compiler - with mixed success. The remains of the attempt didn't survive either. So userspace applications have to be written in assembler which is really difficult.
I'm making the source code and binaries available here unter a do-whatever-you-want-with-it license. Get it here as a ZIP file: ULSOP.ZIP or, if you want to try it out put this floppy image in VirtualBox, KVM or whatever: ULSOP.IMG
Too bad it doesn't just run in DosBox. You could also write it to a floppy and run it on a real PC but I would recommend against it! In FreeDOS it seems to randomly overwrite files like AUTOEXEC.BAT for no reason. This is why I write-protected it on the floppy.
Now on to some screenshots:
The loader initialized everything and is about to switch to graphics mode
The Ulsop "Desktop". Notice the badly (non-) initialized items 8 and 9? You start a program by pressing a number key on the keyboard. Those three vertical lines? That's the mouse pointer. Must be a problem with CuteMouse, it looked better on MS-DOS.
Let's roll some dice. And, by the way, don't use that e-mail address. I check it about once a year or so.
... and play some snake.
There's the game of X's and O's, or in this case Player A and Player B (based on minigui-buttons). Notice there are some extra processes running in the background. I think this was one of the few Ulsop programs I wrote in C because I can't find a TIC.ASM file. No source code, sorry.
If it doesn't crash we can correctly terminate Ulsop.
(Making those screenshots took about three reboots, just to give you an idea of how bad it is)
If you really want to compile Ulsop yourself you need the Symantec C compiler for DOS. It came with my first book on C programming. Ulsop might also compile with its successor, the Digital Mars compiler but I've never tried that. My development environment back then was a Windows 98 DOS window (or just plain DOS), the DOS Editor and Symantec C.