在NASM中,其作用是根据org指令中指定的偏移,计算段内数据的各种偏移,即:在原有地址的基础上加上org指定的偏移形成数据的真正偏移地址。这意味着org指令指定的地址与代码加载地址在原则上是可以不同的,但操作上却又往往不可行,因为一旦加载到了非ORG指定的地址,可能引发的众多数据地址计算的错误,除非代码中未用到对内存的寻址。另外,如果不用org指令,则相当于使用org 0,即段内偏移地址为0,当程序的实际加载地址为0时,程序也可以正常执行。
如下例子:
ORG 7c00h
start: mov ax, message
(other codes)
...
message: db "message1",0
ORG指令指定后面的代码从0x7c00开始执行,假设标号message前面的字节数为 10字节,则message的偏移地址为0x7c0a, mov ax, message,相当于mov ax, 7c0ah. start的偏移地址为0x7c00. 如果不使用ORG 7c00h, 则message的偏移地址为0x000a, 则mov ax, message相当于 mov ax, 000ah
那么为什么要使用ORG指令呢?
一个典型的例子就是Linux操作系统的内核引导代码,当PC上电启动时,BIOS会把启动盘的第一扇区加载到内存0x7c00地址处,然后从此地址开始执行。如果不指定ORG 7c00h,则mov ax, message相当于mov ax, 000ah,这显然是不对的。
如下例子会在屏幕上显示“message1”
;example1.s
;nasm -f bin example1.s
org 7c00h
start:
mov ax, cs ;当前 cs 为 0
mov ds, ax
mov es, ax
call dispstr
jmp $
dispstr:
mov ax,message
mov bp,ax ;es:bp 字符串地址
mov cx,dwMsgNumber ;字符串长度
mov ax,1301h
mov bx,000ch
mov dx, 1004h
int 10h
ret
message: db "message1"
dwMsgNumber equ $ - message ;计算字符串长度
times 510 - ($ - $$) db 0 ;填充程序到510 字节
dw 0AA55h ;
如果我们不使用ORG指令指定偏移,那怎么办呢?看如下例子:
;example2.s
;nasm -f bin example2.s
;
BOOTSEG equ 07c0h
jmp BOOTSEG:start
start:
mov ax, cs ;当前 cs 为 07c0h
mov ds, ax
mov es, ax
call dispstr
jmp $
dispstr:
mov ax,message
mov bp,ax ;es:bp 字符串地址
mov cx,dwMsgNumber ;字符串长度
mov ax,1301h
mov bx,000ch
mov dx, 1004h
int 10h
ret
message: db "message1",
dwMsgNumber equ $ - message ;计算字符串长度
times 510 - ($ - $$) db 0
dw 0AA55h
程序中是用一条跳转指令jmp BOOTSEG:start 跳转到地址 07c0h:start处,虽然此时程序内标号的偏移地址以0为基础,但是JMP指令执行
段间转移,代码段寄存器CS变更为7c0h,
执行以下指令更新DS, ES段的值
mov ax, cs
mov ds, ax
mov es, ax
所以程序中实际的物理地址为 7c00 + 偏移地址。
补充两个实例, 打印寄存器和偏移值:
使用ORG
;Example1
;nasm -f bin Example1.s
;
org 7c00h
[SECTION .code]
[BITS 16]
;
offsetAddr equ $ - $$
start: mov ax,cs
mov ds,ax
mov ax, 0b800h
mov es,ax
mov ah, 0fh
;打印CS的值为:0
mov ax, cs
mov di, ((80 * 9 + 0) * 2)
call DispSetup
mov ax, cs
shr ax, 8
mov di, ((80 * 9 + 2) * 2)
call DispSetup
;打印标号start的值为:7c00h
mov ax, start
mov di, ((80 * 10 + 0) * 2)
call DispSetup
mov ax, start
shr ax, 8
mov di, ((80 * 10 + 2) * 2)
call DispSetup
;打印offsetAddr 的值为: 0
mov ax, offsetAddr
mov di, ((80 * 11 + 0) * 2)
call DispSetup
mov ax, offsetAddr
shr ax, 8
mov di, ((80 * 11 + 2) * 2)
call DispSetup
jmp $
;显示一个8位寄存器的值
;al:数据,di:位置
;如Bl = 0xab, 则显示为ba
DispSetup:
mov cx, 2
mov bl, al
.loop: and al, 0fh
cmp al, 9
ja .1
add al, '0'
jmp .2
.1: sub al, 0ah
add al, 'A'
.2:
.disp: mov ah, 0fh
mov [es:di], ax
add di, 2
mov al, bl
shr al, 4
loop .loop
ret
times 510-($-$$) db 0
dw 0aa55h
不使用ORG
;Example4
;nasm -f bin Example4.s
;
BOOTSEG equ 07c0h
[SECTION .code]
[BITS 16]
jmp BOOTSEG:offsetAddr ;段间跳转,BOOTSEG指示跳转段地址,start指示偏移地址
;即跳转到 7c0h:offsetAddr(7c00h + offsetAddr)地址处
offsetAddr equ $ - $$
start: mov ax,cs
mov ds,ax
mov ax, 0b800h
mov es,ax
mov ah, 0fh
mov ax, cs
mov di, ((80 * 9 + 0) * 2)
call DispSetup
mov ax, cs
shr ax, 8
mov di, ((80 * 9 + 2) * 2)
call DispSetup
mov ax, start
mov di, ((80 * 10 + 0) * 2)
call DispSetup
mov ax, start
shr ax, 8
mov di, ((80 * 10 + 2) * 2)
call DispSetup
mov ax, offsetAddr
mov di, ((80 * 11 + 0) * 2)
call DispSetup
mov ax, offsetAddr
shr ax, 8
mov di, ((80 * 11 + 2) * 2)
call DispSetup
jmp $
;显示一个8位寄存器的值
;al:数据,di:位置
;如Bl = 0xab, 则显示为ba
DispSetup:
mov cx, 2
mov bl, al
.loop: and al, 0fh
cmp al, 9
ja .1
add al, '0'
jmp .2
.1: sub al, 0ah
add al, 'A'
.2:
.disp: mov ah, 0fh
mov [es:di], ax
add di, 2
mov al, bl
shr al, 4
loop .loop
ret
times 510-($-$$) db 0
dw 0aa55h