首页 > 解决方案 > 在 ORG 指令后设置段寄存器

问题描述

我目前正在关注OS development 的教程,其中包括关于引导加载程序的讨论。

我的引导加载程序当前处于 16 位实模式,因此,我可以使用提供的 BIOS 中断(例如 VGA 视频中断等)。

BIOS 提供视频中断0x10(即视频电传输出)。视频中断具有功能0x0E,它允许我在屏幕上打印一个字符。

这是这个基本的引导加载程序:

org     0x7c00              ; Set program start (origin) address location at 0x7c00.
                            ; This program is loaded by the BIOS at 0x7c00.
bits    16                  ; We live in 16-bit Real Mode.

start:  
        jmp loader

bootmsg     db      "Welcome to my Operating System!", 0        ; My data string.

;-------------------------------------------------------
;   Description:    Print a null terminating string
;-------------------------------------------------------
print:
    lodsb                   ; Load string byte at address DS:SI and place in AL.
                            ; Then, increment/decrement SI as defined by the Direction Flag (DF) in FLAGS.
    or      al, al          ; Set the zero flag - is AL zero?
    jz      printdone       ; Check if this is the null byte
    mov     ah, 0eh
    int     10h
    jmp     print
printdone:
    ret

loader:
    ;|---------- Related to my question ----------|
        xor     ax, ax
        mov     ds, ax
        mov     es, ax
    ;|--------------------------------------------|

    mov     si, bootmsg
    call    print

    cli                     ; Clears all interrupts.
    hlt                     ; Halts the system.

times 510 - ($-$$) db 0    ; Make sure our bootloader is 512 bytes large. 

dw      0xAA55              ; Boot signature - Byte 511 is 0xAA and Byte 512 is 0x55, indicated a bootable disk.1

如上面的代码所示,我突出显示了以下三行:

xor     ax, ax
mov     ds, ax
mov     es, ax

根据原始消息来源,它说以下内容:

设置段以确保它们为 0。请记住,我们有 ORG 0x7c00。这意味着所有地址都基于 0x7c00:0。因为数据段在同一个代码段内,所以为空。

我有点困惑。据我了解,该org指令告诉加载程序在 address 加载该程序0x7c00。那我们为什么不把它作为我们的起始地址呢?这意味着,我们的两个重叠的数据和代码段位于基地址为零。基地址应为 0x7c0。为什么作者将基地址设置为0x0?

mov ax, 07c0h
mov dx, ax
mov es, ax

标签: nasmx86-16cpu-registerssegmentmemory-segmentation

解决方案


我一直在研究org更多的说明和其他文档,我了解发生了什么。

根据指令中的NASM 文档i gorg的缩写:

ORG 指令的功能是指定 NASM 将假定程序在加载到内存时开始的源地址。[...] NASM 的 ORG 完全按照指令所说的:起源。它的唯一功能是指定一个偏移量,该偏移量将添加到段内的所有内部地址引用中。

因此,NASM 编译器假定程序将被加载到由原始指令指定的地址(即org)。BIOS 正是这样做的。根据以下内容,一旦 BIOS 找到包含有效引导签名的有效引导扇区,引导加载程序将“加载到内存中的 0x0000:0x7c00(段 0,地址 0x7c00) ”。

从上面的引用中,当 NASM 文档说“内部地址引用”时,它指的是对代码中正在使用的具体内存区域的所有引用(例如引用标签等)。例如,上面引导加载程序代码中的行:mov si, bootmsg将解析bootmsg0x07c00 + offset,其中偏移量由我的字符串的第一个字节的位置bootmsg(即'W')确定。

使用上面的代码,如果我使用ndisasm 实用程序反汇编 bin 文件,我会看到以下内容:

00000000  EB2C              jmp short 0x2e
00000002  57                
00000003  656C              
00000005  636F6D            
00000008  6520746F          
0000000C  206D79            
0000000F  204F70            
00000012  657261            
00000015  7469              
00000017  6E                
00000018  67205379          
0000001C  7374              
0000001E  656D              
00000020  2100              
00000022  AC                lodsb
00000023  08C0              or al,al
00000025  7406              jz 0x2d
00000027  B40E              mov ah,0xe
00000029  CD10              int 0x10
0000002B  EBF5              jmp short 0x22
0000002D  C3                ret
0000002E  31C0              xor ax,ax
00000030  8ED8              mov ds,ax
00000032  8EC0              mov es,ax
00000034  BE027C            mov si,0x7c02
00000037  E8E8FF            call 0x22
0000003A  FA                cli
0000003B  F4                hlt
00000...  ...               ...

(我将生成的指令从 0x00000002 删除到 0x00000020,因为那是我的bootmsg字符串并且代表数据,而不是代码)。

正如我们从输出汇编中看到的,在地址 0x00000034 处,mybootmsg已被替换为 0x7c02(例如 0x7c00 + offset=0x02)。

Michael Petch也提供了一些非常扎实的见解。认为引导加载程序加载到 0x7c0:0x0000(段 0x07c0,偏移量 0)是一种常见的误解。尽管在技术上可以使用它,但它已经标准化为使用零段偏移量来代替(一种好的做法是在引导扇区的一开始就强制执行 CS:IP)。正如迈克尔所提到的,如果想了解更多信息,请查看以下关于段偏移寻址的指南的第 4 节。


推荐阅读