Test that you can directly program your CPU!
Here we'll go over the machine code for printing a character to the
screen using BIOS interrupts. After this page, it should be easy to
write code that prints stuff out to the screen, and at the end, you'll
see the code for printing Hello World.
To print a character, you just need to:
If you have no idea what any of this means, don't worry, that's what this page is for.
At the end of the program, I also put the bytes , which are the same as
The BIOS provides a suite of "interrupt services," which are snippets of code that get run whenever certain "interrupts" are invoked. You can manually invoke an interrupt via the followed by the operand (e.g. ). They're analogous to function calls in other programming languages.
When the BIOS boots up, it registers a bunch of services at different interrupt addresses. These are put there so that whatever program is running can use them to easily interface with peripherals. One of the interrupt services is the Video Services Interrupt which is registered at interrupt address
The full program will look like this:
\b0 A \b4 Sho \cd Dle \eb \fe
MOV al, 'A' MOV ah, 0x0E INT 0x10 JMP $
We'll use the
The , followed by the byte to store into that register. The ASCII character
MOV al, 'A'; Assembles to 0xB0 0x41
This puts the ASCII value for the letter A into the
Next, we need to do something similar with
MOV ah, 0x0E; Assembles to 0xB4 0x0E
Lastly, we need to call the video services interrupt. This needs to be done after the previous two operations, so that the video services interrupt sees the values in
The
INT 0x10; Assembles to 0xCD 0x10
And that is enough, on its own, to print a character, but I always add an infinite loop at the end so that the CPU doesn't start executing whatever nonsense got copied from my flash drive to there.
Once the processor reaches that
I put an infinite loop at the end of my program. I've found that, in
some cases, when my code bugs out or something goes wrong, I'll see
the same program run in a very fast loop. For example, while
trying to print Hello World!, there were a couple times
where e.g. I'd forget the infinite loop at the end and the processor
would just keep printing the text over and over again. It would
instantly fill the screen and then start flashing. The bare minimum
needed here for printing is these 6 bytes (comprising these three
instructions), but in total this program will be 8 bytes with the jump
at the end.
The base opcode for the (short)
The jump's offset operand is always relative to the byte following
the jump instruction, so a jump with the operand zero,
\b4 A \b0 Sho \eb Nul \cd Dle
The jump here has operand 0 (
In this case, the
Another example, looping forever, would be a
\b4 A \b0 Sho \eb \fe \cd Dle
The operand of a
In this code block, the
So to complete the program, I just shove an
On my computer, it takes a few seconds (how long it takes varies) to load, during which I see a flashing caret, then the letter A appears.
Especially if you've already gone through Background, you may want to do something a little more spicy than just printing a character. Like printing multiple characters.
Printing multiple characters is pretty intuitive. After the first character, you've already put
\b4 Sho \b0 H \cd Dle \b0 e \cd Dle \b0 l \cd Dle \cd Dle \b0 o \cd Dle \b0 \cd Dle \b0 W \cd Dle \b0 o \cd Dle \b0 r \cd Dle \b0 l \cd Dle \b0 d \cd Dle \b0 ! \cd Dle \eb \fe
At first glance of this code, you might think there's an
If you tried this and it's not working, there are a couple potential reasons; read these two asides!
Some BIOSes overwrite certain parts of your code when they copy it from your boot drive into memory and then jump to it to start executing. It's very strange. Mine does it: It overwrites bytes
Conventionally, operating systems, bootloaders, and other "boot-level" programs (like ours!) would have a near—not short but near—jump as their first three bytes, and then after the first three bytes, there would be data, called the BIOS parameter block (not code that the CPU is supposed to execute). The near jump would jump over, to some address past that data, to some boot loading code, that would then get executed. This boot code would then read the data located in the BIOS parameter block before it.
Some BIOS implementations, for one reason or another, will try and fill in some of that data with certain values. They expect data to be in the BIOS parameter block, and (I guess for consistency or something?) would overwrite parts of that block with "correct" values for the system they're running on.
The BIOS parameter block is a fairly well-documented concept; you can probably find its structure and format with a quick search online. I never looked into it, so I don't have much to give in the way of it, but I can say that once I found which bytes were being overwritten for me, I just jumped over them (exclusively them, nothing more) and everything's been working fine since.
Copy-pastable code (or something similar) needs to be added here that you can use to check what bytes in memory your motherboard BIOS firmware is overwriting.
You might hear that the BIOS "could" overwrite
You can always check for yourself though by just running code and seeing what happens.
This page gave you a gentle walkthrough into printing characters (using BIOS interrupts), and went over what each "piece" of printing a character is actually for (i.e.
The next page, Print Hex, gives you some code for converting a byte into hexadecimal characters, and then printing it. You can use it to read things like the values in memory or in registers pretty easily.
Here are a few examples of printing junk running:
\b4 Sho \b0 H \cd Dle \b0 i \cd Dle \b0 ! \cd Dle \eb \fe
Hi!.