Hello everybody, it’s me, Lovelace! In this post, you will learn what interrupts are, the Interrupt Vector Table and you'll also implement your own interrupts in Real Mode.
I forgot something past entry and it was to fix an error that might couldn't let us boot on an actual device (even though that entry is all about it), let's fix that error first before we continue to learn about interrupts.
BPB Table taken from osdev.
The problem is, that in some computers our bootloader will boot perfectly fine, but in some others, there might be some errors as the BIOS tampers your data when booting from a USB stick, which is probably the way we all will test our operating system. The reason this happens is because of the BIOS Parameter Block some BIOSes expect it, while some others won't corrupt your data and everything will work fine. The BIOSes that require it will fill this information and that's how our data will be broken.
That data is tampered because when we boot from a USB stick, it is doing something called USB emulation, the BIOS is treating our USB stick as a hard drive and allowing us to talk to it as such. You don't need to know much about the BPB right now, we just need to know that some BIOSes assume it's there and start writing data overwriting your code.
We can get around this problem by implementing the BIOS Parameter Block, we don't need to have real values it can be all zeros, we will create a fake BPB to get around this problem.
The first thing we have to do is to add up the size of the BIOS Parameter Block, except the first three bytes (because they are a short jump and a nop and some BIOSes will look for this as well, we will actually write these first three bytes, and we will rest the other ones with zeros).
To do this, go to the start of your code (before
jmp 0x7c0:start) and create another label, for example:
And what we will do, is basically add:
jmp short start nop
We are doing the short jump and the nop of the first three bytes (Note:
nop means "No Operation").
Now we have to create another label under
start so we can do a jump to that new label so it sets the code segment at
0x7c0, just modify the beginning of your
start label so it looks like this:
start: jmp 0x7c0:step2 step2: ;; Rest of the code here
Note: You also have to delete the old
jmp 0x7c0:start line.
Now, we can put our fake BPB in the middle of
_start and the middle of
start. After the No Operating instruction, let's add 33 zeros (as that's the added num of bytes the BPB occupies):
times 33 db 0
Now you should be able to assemble and boot your bootloader without the risk of code being overwritten by the BPB.
Interrupt Vector Table
What are interrupts?
Interrupts are something like subroutines, but you don't need to know their memory address to invoke them, you call them through the use of interrupt numbers such as
3, rather than memory addresses, interrupts can be set up by the programmer, for example, you could set the interrupt
0x32 to point it somewhere in your code, therefore, when someone does
int 0x32 it will invoke the defined interrupt.
What happens when an interrupt is invoked?
First, the processor gets interrupted, the old state is pushed to the stack, saving it, the old state includes things such as the return address and after that, our interrupt is executed.
What is the Interrupt Vector Table?
The interrupt vector table is a table describing where these interrupts are in memory, we have 256 interrupt handlers and each of those entries in the table is 4 bytes (the first two bytes is the offset in memory, the next two bytes is the segment in memory). Interrupts are also in numerical order in the table.
The interrupt vector table starts to absolute address 0 in RAM, it's the first byte in memory. The first four bytes describe interrupt 0, the next four bytes describe interrupt one, the next four ones interrupt two and so on until interrupt 256. The following is a fictitious IVT:
As you could imagine, each of these interrupts is called by their number (
4, in order) and when the offset and the segment address are added, it will return the absolute address of the routine that executes this interrupt.
Implementing the IVT and creating an interrupt
Go to your
boot.asm file and what we will do, is to create an interrupt. To do so, first create a label:
And what this interrupt will do, is to display a character to the screen so add the following in the
handle_int0: mov ah, 0eh mov al, 'A' mov bx, 0x00 int 0x10 iret
Note: We add the
iret keyword at the end of all the interrupts we defined, it represents the end of an interrupt.
What we are going to do now, is to change the Interrupt Vector table, so when we call the interrupt
0 it will execute our
With what I mentioned before, we know that the interrupt 0 stars at address
0x00 in RAM, so the first two bytes of RAM are the offset for the interrupt, and the next two bytes are the segment for the interrupt 0, so we will only need to do:
mov word [ss:0x00], handle_int0 mov word [ss:0x02], 0x7c0 int 0
After we initialise the registers and we enable the interrupts in the
Note: We use the stack segment (when doing
mov word [ss:hex_num]), because if we don't tell the assembler to use the stack segment, it will use the data segment which currently points at
0x7c0 which would be a problem, that's why we add
ss: which means an address in the stack segment. We could also change the data segment and then set it back with the value it should have, but this way is just easier.
Now if we assemble and run our program, by running:
nasm -f bin boot.asm -o boot.bin qemu-system-x86_64 -hda boot.bin
We would get an output like the following:
The Interrupt 0 is an exception when one number is divided by zero, so what we can do now, is to divide one number by zero and our interrupt will be called when we do that. You can divide by zero doing:
mov ax, 0x00 div ax
Note: Don't forget to delete
That piece of code will set the value of the
ax register to zero and then divide it by itself. Our interrupt would be called again anyway.
You can get more information about the available exceptions here.
You can see this entry's changes here.