Although this answer is late, the question is simple and the code is a good example of a developer assuming that the BIOS jumped to their code with valid or expected values. I have recently answered a number of questions on bootloaders and have begun providing general tips. Tip #1 should apply here:
- When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts.
Since you are making BIOS int
calls you should ensure you set up your own stack. You can't guarantee SS/SP point to enough stack space or is even valid when the BIOS jumps to your code at physical memory address 0x00007C00
. The only requirement is the BIOS starts executing your code at that location, but it doesn't say what CS and IP should be used to do it. The BIOS will also pass the boot drive in DL
.
A single physical memory location can be referenced by many different segment:offset pairs. For example these all point to the boot loader physical address:
0x0000:0x7C00 = (0x0000<<4)+0x7C00 = 0x00007C00
0x0203:0x5BD0 = (0x0203<<4)+0x5BD0 = 0x00007C00
0x07C0:0x0000 = (0x07C0<<4)+0x0000 = 0x00007C00
As you can see the above examples give you an idea that there is more than one way to address a single memory location with segment:offset pairs. The code the poster has presented assumes the CS segment is going to be 0
. One can observe the org 7c00h
in the code, which says that all the absolute near memory references will be offset by 0x7C00
when the compiler generates an object file. Since we will be physically loaded at 0x7C00
in memory, our DS has to be 0x000
.
How does this relate to the OP's code. Observe that we start with:
mov ax, cs
mov ds, ax
mov es, ax
We blindly move what is in CS and transfer it to DS and ES . Our code specifically needs a DS of 0x0000
to work properly (with origin 0x7C00
). I gave some examples of some CS:IP pairs that map to physical location 0x7C00
. If the BIOS jumped to our code specifically with a far jump to 0x0000:0x7C00
, our CS would be 0x0000
and IP would be 0x7C00
. If we moved 0x0000
to DS and ES everything would be fine. What happens though if the BIOS far jumped to 0x07C0:0x0000 ? Our CS would have 0x07C0
in it and IP would be 0x0000
. If we move CS to ES and DS all these segments are 0x07C0
but we wrote our code relative to a segment of 0x0000
.
With the wrong segment what can go wrong? Assume that the Bootmessage
was placed at offset 0x7C50
(0x50 + 0x7C00 origin point) by the assembler. Now execute this code:
mov ax, BootMessage
Since no segment was specified it would have been the same as saying DS:BootMessage
. If our DS segment is set to 0x07C0
and the Bootmessage
set to 0x7C50
we are then accessing physical address (0x07C0<<4)+0x7C50 = 0xF850
. Now it should be obvious - if CS is not 0 we are loading BootMessage from a memory address where our string doesn't exist.
To fix this is simple. Don't assume CS is what we expect. Code the bootloader to use what we want. We intend to have DS and ES set to segment 0x0000
:
xor ax, ax ; Set AX to 0
mov ds, ax ; Initialize DS=ES=0
mov es, ax
Some virtual machines and real hardware are known to far jmp to our bootloader using 0x07C0:0x0000
and some others with 0x0000:0x7C00
. If you assume the value in CS is what you want then your code may work on some platforms and not others.