问题

下面是一段x86主引导分区的汇编程序,在屏幕上打印Hello OS!!!

[org 0x7c00]
; clear secreen
mov ax, 3
int 0x10
; magic break
xchg bx, bx

mov ax, 0xb800
mov es, ax

mov ax, 0
mov ds, ax

mov si , message
mov di, 0
mov cx, (message_end -message)

loop1:
    mov al, [ds:si]
    mov [es:di], al

    inc si
    add di, 2

    loop loop1

halt:
    jmp halt

message:
    db "Hello, OS!!!", 0
message_end:

times 510 - ($ -$$) db 0

db 0x55, 0xaa

关于 org 伪指令的疑问:BIOS将MBR加载到 0x7c00 并跳转到0x7c00执行指令,为什么还需要使用 [org 0x7c00] 指令指定位置?

MBR 执行过程

BIOS在将MBR引导加载到0x7c00物理地址出,然后将 CS:IP 的地址也修改为 0x07c00, 即跳转到MBR所在的内存地址执行指令。

image-20230412130553922

这是执行 mov ax, 0xb800 时指令寄存器指向的物理的值是:CS:IP = 0x0000:0x7c07 ,即 0x07c07(前面还有几条指令)。继续执行(没有写org 0x7c00),可以看到屏幕并没有打印指定的字符

image-20230412131001118

当我们把org 0x7c00 指令加在程序开头就可以正常输出。

image-20230412131833291

org 指令

出现上面的疑问,主要是对org指令和程序执行过程不清楚。org 指令是指定偏移值,如果没有org指令,段内标号的地址偏移就是从 0 开始。对比一下编译后的汇编指令就明白了

org 0x7c00

image-20230412131811321

没有org 0x7c00

image-20230412132003088

上面 mov si, xxx 对应的指令是 mov si , message , org是的messenge的值不同。

在汇编程序中,将标号赋值给一个寄存器 mov si , message , 就是将一个地址值赋值给寄存器。这个地址是一个偏移值,程序在没有加载到内存中之前,我们是无法预知他会出现在内存的哪个物理地址的,所以这是相对程序段开始的偏移地址。比如,这里message: 标号之前的指令总共是0x28个字节,所以message 的值就是 0x28。即 mov si , message 指令就是 mov si, 0x28

后续取数据指令 mov al, [ds:si] (ds = 0x0000) 是从 0x0000:0x0028 处取,但是我们程序(包括定义的数据)是加载到 0x7c00 起始的位置,而数据的位置应该是在 0x07c00+0x0028 ,自然无法取得定义的字符。

在程序首部不加上 org 0x7c00 , 标号的偏移地址就等于 实际字节数+0x7c00 , 这时候 message 的偏移就是 0x7c28 ,这个位置就是我们存放字符的位置。