汇编程序将源代码的每一行转换为处理器指令,并按顺序将这些指令一个接一个地生成到输出二进制文件中。这样做,它维护一个内部计数器,该计数器从 0 开始计数任何此类指令的当前地址。
如果您正在组装一个普通程序,这些指令将最终出现在某个目标文件的代码部分中,其中只有地址的空白槽,之后必须由链接器用正确的地址填充,所以这不是问题。
但是,当您组装一个没有任何节、重定位和其他格式的平面二进制文件时,只有原始机器指令,那么汇编器就没有关于您的标签指示在哪里以及代码和数据的地址是什么的信息。因此,例如,当您有一条指令时mov si, someLabel
,汇编器只能计算该标签在二进制文件开头从 0 开始的偏移量。(即默认是ORG 0
如果你不指定一个。)
如果它不是真的,并且您希望您的机器指令+内存中的数据从其他地址开始,例如7C00
,那么您需要告诉汇编程序您的程序的起始地址是在源代码的开头7C00
写入。org 0x7C00
该指令告诉汇编器它应该启动其内部地址计数器 from7C00
而不是 from 0
。结果是此类程序中使用的所有地址都将移动7C00
. 汇编器只是简单地7C00
将为每个标签计算的每个地址相加。效果就好像标签位于内存中的地址处,例如7C48
( 7C00 + 48
) 而不仅仅是0048
(0000 + 48
),无论它从二进制图像文件的开头偏移仅 48 个字节(在偏移处加载7C00
后将给出正确的地址)。
这些“地址”,如果直接使用jmp si
or mov al, [si]
,是逻辑寻址offset
的一部分,在实模式中,段部分左移 4 以获得偏移添加到的基数。(所以并寻址相同的线性地址,。)该部分来自您放入相关段寄存器的任何内容,或者如果您没有将其设置为固定值,则 BIOS 留在那里的任何内容。seg:off
07C0:000
0000:7C00
7C00
segment
如果您的cs
,ds
和/或es
段寄存器设置为匹配线性地址空间中加载 MBR 的位置(总是7C00
),因此文件的第一个字节位于es:0
例如,使用具有正确设置的段基的偏移量实际上将访问您的数据。 如果已设置jmp si
,将跳转到该标签,您的代码所在的位置也是如此。即,如果引用您的 MBR 的第一个字节。 如果设置正确,将从它加载 2 个字节。cs
cs:si
cs:org
mov ax, [si]
ds
在您的情况下,int 10h
/ah=13h
使用es:bp
,并且绝对寻址没有其他用途,只有相对跳转/调用,其编码不依赖于org
. 出于某种原因,您es
从cs
引导加载程序的开头进行设置,而不是将其设置为固定值以匹配org
您正在使用的值。这是一个错误;您的引导加载程序无法在使用 CS:IP = 跳转到 MBR 的 BIOS 上运行07C0:0000
,只有那些使用0000:7C00
匹配您的org
. mov ax,cs
用xor ax,ax
;替换来解决这个问题 DS / ES是否与CS不同并不重要,这ES: BootMessage-$$ + org
就是您的数据实际所在的位置。
线性地址与逻辑地址
至于你的另一个问题:7C00
是引导加载程序的线性物理地址。您可以用不同的方式将此物理地址表示为逻辑地址(段:偏移),因为段重叠(下一个段10
在前一个段之后开始 16 个字节(十六进制))。例如,您可以使用逻辑地址0000:7C00
,这是最简单的配置:您使用0
从 RAM 开头开始的段,并7C00
从0
. 或者,您可以使用逻辑地址07C0:0000
,即7C0
第 th 段。还记得段开始时彼此相隔 16 个字节吗?因此,您只需将其乘以7C0
(10
十进制16
)即可得到7C00
- 看?这是在您的十六进制地址中向右移动一个位置的问题!:-) 现在你只需添加你的偏移量,也就是0
这次,所以它仍然是7C00
物理的。在内存中开始0
的段中的字节。07C0
7C00
当然,您也可以使用更复杂的地址,例如 ,0234:58C0
这意味着该段开始于2340
并且当您向其添加58C0
偏移量时,您将7C00
再次获得 :-) 但这样做可能会令人困惑。这完全取决于您需要什么配置。如果您想将7C00
物理地址视为段的开始,只需使用段07C0
,您的第一条指令将位于 offset 0
,因此您不需要放置org
指令,或者您可以放置org 0
then。但是,如果您需要读取/写入7C00
地址下方的一些数据(例如,查看 BIOS 数据或摆弄中断向量),则使用段0
和偏移量7C00
这意味着您的第一条指令(二进制文件中的第 0 个字节)将位于7C00
内存中的物理地址;那么您必须org 0x7C00
根据上述原因添加指令。
BIOS 将使用 CS:IP = 07C0:0000 或 0000:7C00 跳转到您的代码。并且在 DS/ES/SS:SP 中有未知值。您应该编写引导加载程序以使其工作,使用xor ax,ax
/mov ds,ax
将 DS 基数设置为零,如果您正在使用org 0x7c00
.
请参阅 Michael Petch 的引导加载程序开发的一般技巧,了解有关编写健壮的引导加载程序的更多信息,以避免对 BIOS 留下的状态做出假设,除了所有 BIOS 必须正确地与主流软件一起工作的那些。(例如,在线性地址 0x00007c00 和 DL 中的驱动器号处加载您的 512 字节 MBR)。
几乎(?)所有 BIOS 都以 CS=0 或 CS=07C0 启动 MBR,而不是其他一些 seg:off 到达相同线性地址的方式。但是你绝对不应该假设其中一个。