NASM 引导加载程序中的 jmp $

Ric*_*wal 2 assembly bios nasm bootloader x86-16

我试图从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
Run Code Online (Sandbox Code Playgroud)

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

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

永远不会被执行。

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

Cra*_*tey 5

$是当前指令的地址,jmp $循环到自身也是如此。这通常是针对致命错误而执行的。

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

db用或dw伪操作定义的事物是数据的定义,而不是可执行指令[通常 - 除非您需要汇编器未知的特殊指令]。

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


偏移量288...

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

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

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

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