问题
下面是一段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所在的内存地址执行指令。
这是执行 mov ax, 0xb800
时指令寄存器指向的物理的值是:CS:IP = 0x0000:0x7c07 ,即 0x07c07(前面还有几条指令)。继续执行(没有写org 0x7c00
),可以看到屏幕并没有打印指定的字符
当我们把org 0x7c00
指令加在程序开头就可以正常输出。
org 指令
出现上面的疑问,主要是对org指令和程序执行过程不清楚。org 指令是指定偏移值,如果没有org指令,段内标号的地址偏移就是从 0 开始。对比一下编译后的汇编指令就明白了
有org 0x7c00
没有org 0x7c00
上面 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
,这个位置就是我们存放字符的位置。