The assembler is translating each line of your sorce code to processor instruction and generates these instructions in sequence, one after another, into the output binary file. Doing that, he maintains an internal counter which counts the current address of any such instruction, starting from 0 and upwards.
If you're assembling a normal program, these instructions will end up in the code section at some object file with just blank slots for addresses, which have to be filled in with proper addresses by the linker afterwards, so it's not a problem.
But when you assemble a flat binary file without any sections, relocations and other formatting, just raw machine instructions, then there is no information for the assembler about where are your labels indicating to and what are the addresses of your data. So, for example, when you have an instruction mov si, someLabel
, then the assembler can only calculate the offset of this label starting from 0 at the beginning of the binary file. That is, it assumes that your code would be located in the memory beginning from the offset 0 in your code segment.
If it's not true, and you want your machine instructions in memory to begin from some other address, eg. 7C00
, then you need to tell the assembler that the starting address of your program is 7C00
by writing org 0x7C00
at the beginning of your source. This directive tells the assembler that it should start up its internal address counter from 7C00
instead of from 0
. The result is that all addresses used in such a program will be shifted by 7C00
. The assembler simply adds 7C00
to each of the address calculated for each label. The effect is as if the label was located in memory at the addres, say, 7C48
(7C00 + 48
) instead of just 0048
(0000 + 48
), no matter that it is offset only 48 bytes from the beginning of the binary image file (which, after loading at the offset 7C00
will give the proper address).
As to your other question: 7C00
is the physical address of the bootloader. You can represent this physical address as a logical address (segment:offset) in a different ways, because segments overlap (next segment starts 16 bytes (10
in hex) after the previous one). For example, you can use logical address 0000:7C00
which is the simplest configuration: you use segment 0
starting at the beginning of your RAM, and offset 7C00
from that 0
. Or, you can use logical address 07C0:0000
, which is 7C0
th segment. Remember that segments start 16 bytes apart from each other? So you simply multiply this 7C0
by 10
(16
in decimal) and you get 7C00
-- see? It's a matter of shift one position to the right in your hexadecimal address! :-) Now you just add your offset, which is 0
this time, so it's still 7C00
physically. The byte 0
in segment 07C0
which starts at 7C00
in memory.
Of course you can also use more complicated addresses, like, for example, 0234:58C0
, which means that the segment starts at 2340
and when you add 58C0
offset to it, you'll get 7C00
again :-) But doing that could be confusing. It all depends on what configuration you need. If you want to consider the 7C00
physical address as the start of your segment, just use segment 07C0
and your first instruction will be at offset 0
, so you don't need to put org
directive, or you can put org 0
then. But if you need to read/write some data below the 7C00
address (for example, peek the BIOS data or fiddle with interrupt vectors), then use segment 0
and offset 7C00
which means your first instruction (0th byte in your binary file) will be located at 7C00
physical address in memory; then you have to add org 0x7C00
directive from the reasons described above.