了解引导加载程序程序集代码和内存位置

时间:2018-09-01 10:19:16

标签: assembly x86-16 bootloader real-mode

我想检查一下我对以下引导程序代码的理解:

BITS 16

start:
    mov ax, 07C0h   ; Set up 4K stack space after this bootloader
    add ax, 288     ; (4096 + 512) / 16 bytes per paragraph
    mov ss, ax
    mov sp, 4096

    mov ax, 07C0h       ; Set data segment to where we're loaded
    mov ds, ax

    mov si, text_string ; Put string position into SI
    call print_string   ; Call our string-printing routine

    jmp $           ; Jump here - infinite loop!

    text_string db 'This is my cool new OS!', 0

print_string:           ; Routine: output string in SI to screen
    mov ah, 0Eh     ; int 10h 'print char' function

.repeat:
    lodsb           ; Get character from string
    cmp al, 0
    je .done        ; If char is zero, end of string
    int 10h         ; Otherwise, print it
    jmp .repeat

.done:
ret

    times 510-($-$$) db 0   ; Pad remainder of boot sector with 0s
    dw 0xAA55       ; The standard PC boot signature

这是我对代码的理解:

  

移动斧头,07C0h:

  • 可以从堆栈段值(存储在ss寄存器中)和偏移量(存储在sp寄存器中)获得内存地址。通过执行以下操作获取地址:堆栈段值x 16 +偏移值。
  • 我们正在使用段07C0h在我们的代码中设置空间。因此,当07C0h偏移为0时,它将引用地址为0x7C00。 BIOS尝试从0x7C00引导代码。 MBR是在0x7C00中复制的代码示例。
  • 每个段以16字节为块递增,因此07C0h将为您提供地址范围0x7C00-0x7C0F。下一个段07C1h将为您提供地址0x7C10-0x7C1F。
  

添加斧头,288

  • 要在引导加载程序之后设置4K堆栈空间,我们需要添加每段从(4096 + 512)/ 16字节获得的288。十进制值288等于120h。
  • 现在存储在ax寄存器中的值为08e0h,可通过以下方式获得:07c0h + 120h = 08e0h(120h为十进制288)。
  

mov ss,ax

  • 将寄存器ax中的值复制到ss寄存器中。偏移量现在包含段:08e0h。
  

mov sp,4096

  • 偏移量值为4096,即0x1000h。 ss:sp对的值为08e0:1000。
  • 堆栈的底部从内存地址0x8e00开始,堆栈的顶部从0x9e00(从0x8e00 + 0x1000 = 0x9e00)开始。

下面是内存中的代码和分配的空间的图表,如下所示。

enter image description here

注意:此图中的引导程序和内存引用很可能是错误的,我假设它不是顺序的,并且汇编程序将以不同的方式编译机器代码。但是,代码的开始将从较低的内存地址(0x7C00)开始,引导签名将从较高的地址(0xaa55磁盘签名从0x7c0:0x1fe(物理地址0x7c0 * 16 + 0x1fe = 0x7dfe)开始第一个512字节扇区的最后两个字节,从0x7c0:0x0000到0x7c0:0x200(0x7C32,即512字节的末尾))。

奇怪的是,看到分配的空间是两个4096字节的块:一个用于堆栈,另一个包含代码和数据。我怀疑我在这里错过了一些东西。

1 个答案:

答案 0 :(得分:1)

  

以下是引导加载程序代码:

缺少有关如何设置汇编代码的相对地址的信息,即如何计算局部偏移量。通常,引导加载程序以org 0x7C00开头,以明确表明期望从cs:ip = 0000:7C00开始的代码。但是您会这样做,ds=07C0会是错误的,因为有人建议代码期望偏移量像从07C0:0000而不是0000:7C00开始一样被汇编。虽然两个地址都针对相同的物理内存地址,但是segment:offset对却不同。

  

每个段以16字节为块递增,因此07C0h将为您提供地址范围0x7C00-0x7C0F。下一个段07C1h将为您提供地址0x7C10-0x7C1F。

每个段为您提供64kiB的范围,尽管起始地址仅增加16个字节,所以段之间有很多重叠,您可以通过许多组合来寻址相同的物理地址。即ds=07C0使您可以进入物理内存范围07C00-17BFF


然后,您将值转换为十六进制是错误的(另请参阅Michael注释),288 = 0x120,以及4096 = 0x1000,但是您正确地得出结论:512B的引导加载程序代码(块设备的单个扇区),4096B的备用空间,然后是4096B的堆栈空间。您是否希望通过向堆栈中推入4096个以上的字节来填充堆栈,所以它不会在代码执行后到达备用空间,而是会缠绕到08E0:FFFE(远高于堆栈的原始开头)。

  

我假设它不是顺序的,汇编器将以不同的方式编译机器代码。

与之相反,指令和源代码中定义的字节在生成的机器代码中顺序发出。使用“列表”命令行开关来查看汇编程序如何发出特定行的机器代码。例如,您是否将text_string db 'This is my cool new OS!', 0行移到BITS 16指令之后的开头,那么该文本将位于机器代码的开头,由BIOS从0000:7C00地址加载和执行,执行文本字节作为指令。