【问题标题】:jmp $ in NASM bootloaderNASM 引导加载程序中的 jmp $
【发布时间】:2022-01-07 03:29:45
【问题描述】:

我试图从Bootloader 编写引导加载程序。写的代码是

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

我不明白我们为什么要写 jmp$。通过编写 jmp$ 它进入一个无限循环。所以,进入无限循环后,最后两行

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

永远不会被执行。

此外,我们为什么要在 ax 中添加 288?

【问题讨论】:

  • 好吧,它在打印后没有任何事情可做,所以无限循环是它可以做的最好的事情,以避免执行您的文本字符串及其之后的任何内容。
  • 将 288 添加到 0x7c0 并将其存储在堆栈段中只是在堆栈和引导加载程序之间放置空间。中间的内存区域可以用作磁盘缓冲区等。
  • 您可能还会遇到jmp $ 的另一个变体是序列cli endloop: hlt jmp endloop

标签: assembly nasm bootloader bios x86-16


【解决方案1】:

最后两个语句不是可执行语句,它们是一种伪指令或汇编指令。在这里,它们用于组装目标代码而不是一些可执行指令,并确保引导扇区为 512 字节。

jmp $ 然而是一个可执行指令,并且会在加载器加载它之后运行。而最后两行是由汇编器在将汇编代码汇编成目标文件时使用(而不是执行)的。

【讨论】:

    【解决方案2】:

    $ 是当前指令的地址,所以jmp $ 正在循环到自己。这通常是针对致命错误执行的。

    这里的加载器是不完整的,所以它正在输出一条消息然后循环。 “循环”指令[希望]将被实际代码[待添加]替换。

    dbdw 伪操作定义的东西是 data 的定义,并且是 not 可执行指令 [通常——除非你需要特殊指令汇编器不知道]。

    因此,如果没有无限循环,您将尝试执行text_string: 处的数据,这会产生未定义/意外的结果,更不用说尝试执行引导块的最后部分了。


    288 偏移量...

    引导加载到地址0x07C00。它试图在位置(0x07C00 + 4096 + 512) --> 0x8E00 设置它的堆栈段。但是,它试图将其放入段寄存器中,因此该值必须右移 4 位。 0x07C0已经移位,288(4096 + 512) >> 40x0120SS 的最终值为0x07C0 + 0x0120 --> 0x08E0 [地址0x8E00]

    这似乎是错误的(即算术不匹配),但sp 寄存器设置为4096,因此ss:sp 的最后存放位置是地址0x9E00

    在 8086 实模式寻址中,所有地址都使用段寄存器和一些偏移量。最终地址为:address = (segreg << 4) + offset。这是由硬件在以某种方式访问​​内存的每条指令上完成的。

    当您在代码中跳转时,您使用CS [代码段] 寄存器。数据访问使用DS [数据段] 寄存器。和堆栈访问(例如push/pop%sp 相对,使用SS [堆栈段] 寄存器。还有一个ES [额外段] 寄存器用于字符串指令。

    【讨论】:

    • 结果已定义,只是可能出乎意料。
    • 也就是说最后两行永远不会被执行?
    • @RichaTibrewal 对。您也不希望它们成为 not 指令。执行 dw 就像在 C 中执行 unsigned short xx = 0xAA55; 一样。您不会尝试(在 C 中)执行它吗?
    • 能否也解释一下前六行代码?
    • @MichaelPetch 每次我[之后]进行编辑时,我都意识到数学是错误的。我是手工完成的,然后是 kcalc,最后是 perl。我几乎要添加免责声明:“十六进制数学不准确,我很累”:-)
    猜你喜欢
    • 2013-07-07
    • 2012-04-14
    • 2011-12-08
    • 2017-01-07
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2021-03-30
    相关资源
    最近更新 更多