x86 adventures | download | source
Let's jump a few years. This little demo shows a x86 emulator in webassembly. The thing would be about 1000x faster in a decent emulator, but it should be fine for our demonstration purposes. If you want the full experience, download the floppy image and drop it into your favourite emulator or a real PC.
First steps
I always wanted to write my own bootloader. (In fact this is what inspired the whole Manderbrot saga.) It looks so cryptic and hardcore, isn't it? How does a PC start?
Back in the days (before UEFI) when a PC was booting it started to look around on the storage devices for something that looked like a 'boot sector'. It was loading the first sector (512 bytes) from each disk and if a sector ended with the magic bytes 0xAA55
it supposed that it's a boot sector. It loaded it into a specified place in memory and just executed it. What could go wrong?
If it really was a boot sector of some OS, the OS continued the boot process with loading different parts of itself and finally 'starting' the machine.
Programming
I installed Qemu and VirtualBox for development and used the NASM assembler.
Since I was writing a bootloader I could not use the convenience functions of an OS. I had to do everything by hand. The BIOS that comes with the PC can do a couple of things but really not that much, a little bit of input and output to the screen or other devices, just as its name suggests (Basic IO).
The Mandelbrot set uses graphics, floating point operations and I also wanted to add mouse support. This doesn't fit into the 512 bytes of the boot sector, so I had to write a two-phase bootloader. The first 512 byte just loads the real stuff...
I never used floating point in assembly before, and I couldn't really debug the thing. At the end I had to create some small test output that printed out the 64 bit representation of the floats and used a website to translate them to human readable form.
Once it worked well in Qemu I switched to VirtualBox. It failed miserably.... After a long debug session I realised that if the floating point register stack overflows in VirtualBox it will just return NaN-s for each subsequent operation... I think that's how it should behave and Qemu was a bit more generous with me. Another lesson learned the hard way.
Mouse support
Today the mouse is part of our life, but it wasn't that common in the 1980s. One had to install a mouse driver that came with his mouse on a floppy disk to make it work under MS DOS. Play with things like config.sys
and autoexec.bat
to make it work.
I found a PS/2 compatible mouse driver on StackOverflow which registers a callback through the 15h
interrupt and listens to mouse events. Of course PS/2 is history now, but I heard the USB mouse simulates its behavior. I don't know, but it certainly worked in VirtualBox and Qemu.
Getting the mouse events is one thing, but we also need to draw it somehow. At this point I only knew that the mouse was moved down by say 5 pixels, but nothing else.
I had to draw my mouse cursor, and come up with a mechanism to save the area that is under the mouse so that I can redraw it later when it is moved. And what if the Mandelbrot drawer wants to draw 'under the mouse'? Another case to be handled... What if it's partially out of screen?
Imagine some really hardcode stuff here.
Real hardware
The section wouldn't be complete without a test on real hardware. I asked around on the net and found the vintage computing reddit group. where u/istarian was kind enough to try it on his old laptop successfully booting from a floppy disk.
Source: https://imgur.com/gallery/3RPSca1
This is a Dell Latitude C600 (Pentium III 1 GHz, 512 MB RAM).
Later u/ilTrolloso tried it on a few more machines. It seems that my code doesn't work on Pentium MMX machines, because I used a special compare instruction that was introduced in the Pentium 3. This image is taken from a Thinkpad T23 (Pentium III@1133MHz):
Source: https://imgur.com/a/ySZvKGO