4

最近我在学习如何编写引导扇区,这是我正在学习的完整代码:

org 07c00h
    mov ax, cs
    mov ds, ax
    mov es, ax
    call DispStr
    jmp $

DispStr:
    mov ax, BootMessage
    mov bp, ax
    mov cx, 16
    mov ax, 01301h
    mov bx, 000ch
    mov dl, 0
    int 10h
    ret

BootMessage: db "Hello, OS!"
times 510-($-$$) db 0

dw 0xaa55

如果您知道如何引导系统,则代码非常简单。结果是Hello OS!屏幕上显示的一行,我唯一不知道的是第一行:org 07c00h

书上告诉我这行代码让编译器定位到7c00h这个地方的地址,但是解释的很模糊,真不知道这里有什么用。这条线到底在org 07c00h做什么?

我试图删除该行,并使用 nasm 创建一个bin文件,然后使用 bochs 启动 bin 文件。与上一个没有什么不同:“hello OS!” 也显示在屏幕上。

我可以说第一行在这里什么都不做吗?有什么用org xxxx

4

2 回答 2

8

汇编程序将源代码的每一行转换为处理器指令,并按顺序将这些指令一个接一个地生成到输出二进制文件中。这样做,它维护一个内部计数器,该计数器从 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 sior mov al, [si],是逻辑寻址offset的一部分,在实模式中,段部分左移 4 以获得偏移添加到的基数。(所以并寻址相同的线性地址,。)该部分来自您放入相关段寄存器的任何内容,或者如果您没有将其设置为固定值,则 BIOS 留在那里的任何内容。seg:off 07C0:0000000:7C00 7C00segment

如果您的cs,ds和/或es段寄存器设置为匹配线性地址空间中加载 MBR 的位置(总是7C00),因此文件的第一个字节位于es:0例如,使用具有正确设置的段基的偏移量实际上将访问您的数据。 如果已设置jmp si,将跳转到该标签,您的代码所在的位置也是如此。即,如果引用您的 MBR 的第一个字节。 如果设置正确,将从它加载 2 个字节。cscs:sics:orgmov ax, [si]ds

在您的情况下,int 10h/ah=13h使用es:bp,并且绝对寻址没有其他用途,只有相对跳转/调用,其编码不依赖于org. 出于某种原因,您escs引导加载程序的开头进行设置,而不是将其设置为固定值以匹配org您正在使用的值。这是一个错误;您的引导加载程序无法在使用 CS:IP = 跳转到 MBR 的 BIOS 上运行07C0:0000,只有那些使用0000:7C00匹配您的org. mov ax,csxor ax,ax;替换来解决这个问题 DS / ES是否与CS不同并不重要,这ES: BootMessage-$$ + org就是您的数据实际所在的位置。


线性地址与逻辑地址

至于你的另一个问题:7C00是引导加载程序的线性物理地址。您可以用不同的方式将此物理地址表示为逻辑地址(段:偏移),因为段重叠(下一个段10在前一个段之后开始 16 个字节(十六进制))。例如,您可以使用逻辑地址0000:7C00,这是最简单的配置:您使用0从 RAM 开头开始的段,并7C000. 或者,您可以使用逻辑地址07C0:0000,即7C0第 th 段。还记得段开始时彼此相隔 16 个字节吗?因此,您只需将其乘以7C010十进制16)即可得到7C00 - 看?这是在您的十六进制地址中向右移动一个位置的问题!:-) 现在你只需添加你的偏移量,也就是0这次,所以它仍然是7C00物理的。在内存中开始0的段中的字节。07C07C00

当然,您也可以使用更复杂的地址,例如 ,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 到达相同线性地址的方式。但是你绝对不应该假设其中一个。

于 2012-06-17T22:15:24.927 回答
4

在这里,您可以一步获得汇编器和链接器。org 告诉汇编器,它告诉链接器(在这些情况下通常是同一个程序)将后面的代码放在物理内存空间的哪个位置。当您使用 C 编译器或其他一些高级语言编译器时,您通常有单独的编译和链接步骤(尽管编译器经常在幕后为您调用链接器)。源代码被编译为与位置无关的目标文件,其中一些指令在链接步骤中等待未实现。链接器从描述内存空间的用户那里获取对象和链接描述文件或信息,然后从那里编码该内存空间的指令。

User786653 设置得很好,它告诉汇编程序它自己无法弄清楚这些指令将存在的内存空间/地址,以防需要在指令中进行位置相关编码。如果它是包含地址信息的二进制文件,例如 elf、srec、ihex 等,它也会在输出二进制文件中使用该信息。

于 2012-04-24T17:26:37.523 回答