nasm 与 masm语法区别

看到一篇文章,是介绍nasm语法的:http://blog.csdn.net/hitop0609/article/details/4329454

masm是微软专门为windows下汇编而写的,而nasm可以在windows、linux等系统下汇编,故而个人推荐使用nasm。

3.1 nasm 是区分大小写

例如:符号 fooFOO 是两个不同的标识符。

3.2 内存操作数表达式

3.2.1 在 nasm 语法里,对 memory 操作数需要加 [ ] 括号

下面的代码:

foo    equ    1
bar    dw     2

    bits 32

    mov eax, foo
    mov ebx, bar

第 2 指令的意图是:将 bar 内 的值赋给 ebx 寄存器。但这样是错误的,nasm 只会把 bar 当作是 immediate 赋给 ebx

00000000  0200                            ; bar 变量
00000002  B801000000        mov eax,0x1
00000007  BB00000000         mov ebx,0x0  ; 将 bar 地址作为 immediate 赋给 ebx 

因此,需要将 bar[ ] 括起来

mov eax, foo
mov ebx, [bar]

nasm 就编译出正确的代码:

00000000  0200                                    ; bar
00000002  B801000000        mov eax,0x1
00000007  8B1D00000000       mov ebx,[dword 0x0]    ; bar 的内容赋给 ebx

3.2.2 给 memory 操作数提供一个 displacement 值

下面代码展示了 [base + disp] 的寻址方式:

section .bss

buffer  resb 10

    section .text

    bits 32

    mov byte [buffer + 0x01] , ‘a‘
    mov ebx, buffer
    movzx eax, byte [ebx + 0x01] 

3.2.3 指明 memory 操作数的 operand size

下面展示了为 memory 操作数提供一个 size 情况:

mov byte [buffer + 0x01] , ‘a‘
mov ebx, buffer
movzx eax, byte [ebx + 0x01]

代码中使用 byte 关键字对 memory 操作数进行了修饰,指明 memory 操作数的大小为 byte

在这方面,nasm 的语法与微软的 masm 的语法(Intel 语法)有些不同,masm 的语法是:

mov byte ptr [buffer + 0x01] , ‘a‘
mov ebx, buffer
movzx eax, byte ptr [ebx + 0x01]

在 masm 语法中需配合 ptr 指示字。

3.2.4 提供一个 segment

大多数 指令/内存操作数 缺省的 segment 是 DS ,x86/x64 允许为 memory 操作数提供另一个 segment 进行 segment override

在 nasm 语法中,如下:

mov byte [es: buffer + 0x01] , ‘a‘
mov ebx, buffer
movzx eax, byte [es: ebx + 0x01]

nasm 语法中,在 [ ] 括号内提供 segment ,不能在 [ ] 括号外提供 segment

而 masm 的语法中是在 [ ] 括号外提供 segment,如下:

    mov byte ptr es: [buffer + 0x01] , ‘a‘
    mov ebx, buffer
    movzx eax, byte ptr es: [ebx + 0x01]

3.2.5 指明 memory 操作数的 address size

有些情况下必须指明 memory 操作数的 address size ,否则编译结果可能不是你想要的结果。下面的例子说明,如何为 memory 操作数指明 address size

例 1:

    section .bss

    buffer resb 10

    section .text

    bits 64

    mov rax, [qword buffer]        ; 指明 64 位的 address size(绝对地址)
    mov rax, [buffer]              ; 使用 32 位的 address size(绝对地址)

在 nasm 中,对于 绝对地址 形式,缺省是 32 位的,因此,需要明确使用 qword 来指明 64 位的 address size

这段代码编译后为:

00000000  48A11400000000000000   mov rax,[qword 0x14]    ; 64 位地址
0000000A  488B042514000000       mov rax,[0x14]          ; 32 位地址

它们的区别就是一个使用了 64 位地址,一个使用了 32 位地址。

例 2:

    section .bss

    buffer resb 10

    section .text

    bits 16

    mov byte [es:dword buffer + 0x01], ‘a‘          ; 指明为 32 位地址
    mov ebx, buffer
    movzx eax, byte [es:ebx+0x01]           

上面代码是 16 位代码,使用了 dword 指明 memory 操作数是 32 位的地址。

它被编译为:

00000000  2667C6051D000000   mov byte [dword es:0x1d],0x61
         -61
00000009  66BB1C000000      mov ebx,0x1c
0000000F  2667660FB6830100  movzx eax,byte [es:ebx+0x1]
         -0000

16 位的 address size 被 override 为 32 位地址。

3.3 nasm 伪指令

伪指令不是 x86/x64 机器的真实指令,伪指令是用于给编译器指示如何进行编译。

3.3.1 nasm 定义的 7 种数据 size

  • byte8
  • word 16
  • dword 32
  • qword 64
  • tword80
  • oword 128
  • yword 256

oword 可以对应 Microsoft MASM 的 xmmword 类型,yword 对应 Microsoft MASM 的 ymmword 类型。

tword, oword 以及 yword 使用在 非整型 数据,使用在 float SSE 型数据。

3.3.2 定义初始化数据:db 家族

nasm 定义了用于初始化上面 7 种 size 的 db 家族,它们用于定义初化常量值。

  • db : define byte
  • dw :define word
  • dd :define doubleword
  • dq :define quadword
  • dt :define tword
  • do :define oword
  • dy :define yword

正如前面所说的:dt , do , dy 不接受整型数值常量,它们被使用在定义 float 或 SSE 数据常量。dt 可以定义 extended-precision float 数据,do 可以定义 quad-precision float,dy 可定义 ymm 数据。而 dq 可以定义 double-precision float 数据,dd 可以定义 single-precision float 数据。

下面是 NASM Manual 上的例子:

      db    0x55                ; just the byte 0x55
      db    0x55,0x56,0x57      ; three bytes in succession
      db    ‘a‘,0x55            ; character constants are OK
      db    ‘hello‘,13,10,‘$‘   ; so are string constants
      dw    0x1234              ; 0x34 0x12
      dw    ‘a‘                 ; 0x61 0x00 (it‘s just a number)
      dw    ‘ab‘                ; 0x61 0x62 (character constant)
      dw    ‘abc‘               ; 0x61 0x62 0x63 0x00 (string)
      dd    0x12345678          ; 0x78 0x56 0x34 0x12
      dd    1.234567e20         ; floating-point constant
      dq    0x123456789abcdef0  ; eight byte constant
      dq    1.234567e20         ; double-precision float
      dt    1.234567e20         ; extended-precision float

3.3.3 定义非初始化数据:resb 家族

程序中使用到的非初始化数据通常放在 bss section 里,bss 代表 uninitialized storage space

nasm 使用了 resb (reserve byte) 家族来定义非初始化数据。

  • resb :reserve byte
  • resw :reserve word
  • resd :reserve doubword
  • resq :reserve quadword
  • rest :reserve tword
  • reso :reserve oword
  • resy :reserve yword

resb 相当于 Microsoft MASM 语法中的 db ?

下面是 NASM Manual 的例子:

buffer:         resb    64              ; reserve 64 bytes
wordvar:        resw    1               ; reserve a word
realarray       resq    10              ; array of ten reals
ymmval:         resy    1               ; one YMM register

3.3.4 包含 binary 文件

nasm 提供了一种包含 binary(二进制)文件的方法:使用 incbin 伪指令。incbin 伪指令包含的 binary 文件将直将写入输出文件中。此伪指令的作用是包含 graphics 以及 sound 这类数据文件。

    incbin  "file.dat"             ; include the whole file
    incbin  "file.dat",1024        ; skip the first 1024 bytes
    incbin  "file.dat",1024,512    ; skip the first 1024, and
                                   ; actually include at most 512

3.3.5 使用 equ 定义常量

equ 用来为标识符定义一个 整型 常量,它的作用类似 C 语言中的 #define

a  equ 0                          ; OK
b  equ ‘abcd‘                     ; OK! b = 0x64636261
c  equ ‘abcdefghi‘                ; warning! c = 0x6867666564636261
d  equ 1.2                        ; error!

    section .data

string db ‘hello,word‘,0
len    equ $-string               ; OK! len = 0x0b

    section .text
textlen equ  _end - entry         ; OK! textlen = 0x05

_entry:
    mov ecx, textlen

_end: 

例子中: b 定义为常量 ‘abcd‘ 它将是字符串的 ASCII 码序列,‘abcdefghi‘ 常量将会被截断,整型常量最长为 quadword(8 bytes),而 d 企图被定义为一个 float 常量,这产生会错误。len 和 textlen 被定义为编译期确定的数值。

3.3.6 使用 times 重复写数据或指令

times 是一个比较实用伪指令,用来重复定义数据或指令。

下面是一个经典的使用例子:

times 510-($-$$) db 0

 dw 0xaa55

这段代码经常出现在 boot 磁盘 MBR 引导代码中,目的是除了最后 2 个字节和 code 代码外的区域全部写 0 值。

times 还可以使用在重复写某一条指令,如下:

times 10 nop

这段代码结果是重复填入了 10 条 nop 指令:

00000000  90                nop
00000001  90                nop
00000002  90                nop
00000003  90                nop
00000004  90                nop
00000005  90                nop
00000006  90                nop
00000007  90                nop
00000008  90                nop
00000009  90                nop

3.4 常量值

nasm 下可以接受 4 种常量:整型常量字符常量字符串常量 以及浮点常量

3.4.1 整型常量

在 nasm 中,常用的整型进制有 4 种:

  • decimal :十进制数
  • hex    :十六进制
  • binary  :二进制数
  • octal  :八进制数

每一种进制都有前缀后缀 表示法。当数值无前缀和后缀时,它是十进制数。因为缺省是十进制。

3.4.1.1 十进制数表示方法

看一看,下面的例子:

        mov     ax,200          ; decimal
        mov     ax,0200         ; still decimal
        mov     ax,0200d        ; explicitly decimal
        mov     ax,0d200        ; also decimal 

十进制的前缀是:0d , 后缀是:d

3.4.1.2 十六进制数表示方法

十六进制使用 0123456789ABCDEF 来表示 16 个数值。类似地,它的前缀是:0h 0x (c/c++ 风格)以及 $0 (pascal 风格),后缀是:h

    mov ax, 0c8h         ; hex
    mov ax, 8h           ; hex

上面例子中的 hex 数,表明:当以 h 后缀结尾时,如果含有字母 ,必须要以 0 开头。

 mov     ax,$0c8         ; hex again: the 0 is required 

上面是 pascal 风格的十六进制表示法,使用前缀 $0 (0 是必须的)

        mov     ax,0xc8         ; hex yet again
        mov     ax,0hc8         ; still hex 

上面是使用 0h 前缀和 C/C++ 风格的 0x 表示十六进制数。

3.4.1.3 八进制数表示方法

八进制的前缀可以是:0o0q 后缀可以是:oq

        mov     ax,310q         ; octal
        mov     ax,310o         ; octal again
        mov     ax,0o310        ; octal yet again
        mov     ax,0q310        ; octal yet again 

3.4.1.4 二进制数表示方法

类似地,二进制的前缀是:0b 后缀是:b

        mov     ax,11001000b    ; binary
        mov     ax,1100_1000b   ; same binary constant
        mov     ax,0b1100_1000  ; same binary constant yet again

3.4.2 字符常量

在 nasm 中,可以使用 3 种引号来提供字符

  • ... (单引号)
  • " ..." (双引号)
  • ` ...` (反引号)

如下示例,它们的结果是一样的:

        db  ‘abcd‘
        db  "abcd"
        db  `abcd`

3.4.2.1 提供字符常量

下面看看如何提供字符量:

          mov eax, ‘a‘                       ; eax = 0x61
          mov eax, ‘abcd‘                    ; eax = 0x64636261
          mov eax, ‘abcdefghi‘               ; eax = 0x64636261
          mov eax, `/x61/x62/x63/x64‘        ; eax = 0x64636261   

第 3 条企图赋超过 4 bytes 的字符常量给 eax, 编译器会截断为 4bytes 再赋给 eax, 而第 4 条是另一种字符常量表示法,使用转义字符表示。

可见:字符常量是以 little-endian 排列

3.4.3 nasm 中的转义字符

在 nasm 中使用 c 风格的转义字符,在 / (反斜杠符)后面跟 转义码/ escape-code

转义码(escape-code)包括:字符转义码 , 八进制转义码 , 十六进制转义码

注意:nasm 中的转义符必须要用 ` `(反引号)来引用

    db  `/x61`     ; right!  ‘a‘
    db  ‘/x61‘     ; wrong!  ‘/‘ , ‘x‘ , ‘6‘, ‘1‘

第 1 个用反引号来包含转义符是正确的。而第 2 个用单引号来包含转义符,nasm 却视它为一般的字符串对待

3.4.3.1 字符转义码

下面是一些例子:

      /‘          single quote (‘)
      /"          double quote (")
      /`          backquote (`)
      //          backslash (/)
      /?          question mark (?)
      /a          BEL (ASCII 7)
      /b          BS  (ASCII 8)
      /t          TAB (ASCII 9)
      /n          LF  (ASCII 10)
      /v          VT  (ASCII 11)
      /f          FF  (ASCII 12)
      /r          CR  (ASCII 13)
      /e          ESC (ASCII 27) 

3.4.3.2 八进制转义码

/ 后面最多可以跟着 3 位数字,构成八进制转义码,如下所示:

    /377        Up to 3 octal digits - literal byte
    /004        ; EOT
    /006        ; ACK
    /025        ; NAK
    /0          ; NULL

3.4.3.3 十六进制转义码

十六进制转义码以 x X 开头,如下所示:

    db `/x61`                            ; ‘a‘
    db `/x61/x62/x63/x64`                 ; ‘abcd‘    

上面所示:十六进制转义码可以连串提供。

3.4.4 字符串常量

字符串是逐个逐个提供字符,看以下例子:

      db    ‘hello‘               ; string constant
      db    ‘h‘,‘e‘,‘l‘,‘l‘,‘o‘   ; equivalent character constants

提供字符串常量,是从左到右依次写入到内存。

这里要注意的是:字符常量 与字符串常量 的区别 ,看一看下面的例子

buffer    db ‘hello‘                     ; 字符串常量
          ... ...

          mov eax, ‘hello‘               ; 字符常量       

字符常量与字符串常量最大区别就是:字符常以 littlen-endian 存储,而字符串常量是从左到度

3.4.5 Unicode 字符串

nasm 定义了两个操作数符来定义 Unicode 字符串:

  • __utf16__
  • __utf32__

下面是 nasm 里的例子:

%define u(x) __utf16__(x)
%define w(x) __utf32__(x)

      dw u(‘C:/WINDOWS‘), 0       ; Pathname in UTF-16
      dd w(`A + B = /u206a`), 0   ; String in UTF-32

3.4.6 浮点数常量

浮点数变量 可以使用 DB , DW , DD , DQ , DT 以及 DO浮点数常量 使用 __float8__ , __float16__ , __float32__ , __float64__ , __float80m__ , __float80e__ , __float128l__ 以及 __float128h__ 来定义。

下面是 nasm 提供的例子:

      db    -0.2                    ; "Quarter precision"
      dw    -0.5                    ; IEEE 754r/SSE5 half precision
      dd    1.2                     ; an easy one
      dd    1.222_222_222           ; underscores are permitted
      dd    0x1p+2                  ; 1.0x2^2 = 4.0
      dq    0x1p+32                 ; 1.0x2^32 = 4 294 967 296.0
      dq    1.e10                   ; 10 000 000 000.0
      dq    1.e+10                  ; synonymous with 1.e10
      dq    1.e-10                  ; 0.000 000 000 1
      dt    3.141592653589793238462 ; pi
      do    1.e+4000                ; IEEE 754r quad precision

3.5 表达式

在 nasm 里的表达式很像 C 表达式,对于熟悉 C 表达式的人来说几乎可以马上上手。

3.5.1 $ 与 $$ 标号

$ 标号表示 nasm 编译后当前指令位置

$$ 标号表示当前 section 起始位置

看看下面的例子:

bits 64

section .rdata
    dq 0

section .text

    mov rax, 0
    mov rax, $-$$

它的编译结果是:

00000000  48B8000000000000  mov rax,0x0
         -0000
0000000A   48B80A0000000000  mov rax,0xa
         -0000
00000014  0000              add [rax],al
00000016  0000              add [rax],al
00000018  0000              add [rax],al
0000001A  0000              add [rax],al

3.5.2 位运算符

与 C 一样,包括下面:


位运算符

描述

&

位与: AND

|

位或:OR

~

位非: NOT

^

位异或: XOR

<<

左移

>>

右移

3.5.3 算术运算符

下面是 nasm 所支持的算术运算符


算术运算符

描述

+


-


*


/

除(无符号数)

//

除(符号数)

%

取模(无符号数)

%%

取模(符号数)
时间: 2024-10-12 04:38:21

nasm 与 masm语法区别的相关文章

Python2和Python3的一些语法区别

Python2和Python3的一些语法区别 python Python2和Python3的一些语法区别 1.print 2.input 3. python3版本相对2版本的部分其他区别 问题:为何会出现乱码的情况 问题:如何获取编码方式的信息? 问题:在控制台上看到的到底是什么? 1.print 在版本2的使用方法是: print 'this is version 2 也可以是 print('this is version 2') 但到了3,就只能加上括号,像一个函数一样来使用 print:

C++与Java的语法区别

C++与Java的语法区别 首先,两个大的不同是主函数和怎样编译的不同,接下来是许多小的区别. main 函数C++//自由浮动的函数int main( int argc, char* argv[]){    printf( "Hello, world" );}Java// 每个函数都必须是一个类的一部分;当java <class>运行是一个特定类的主函数会被调用// (因此你可以让每个类都有一个main函数,这在写单元测试是很有用)class HelloWorld{   

groovy与java语法区别

groovy与java语法区别 Groovy试图对Java开发人员尽可能地兼容.在设计Groovy时,遵循最不意外的原则,尤其是对于那些来自Java背景的开发人员. 这里我们列出了Java和Groovy之间的所有主要区别. 1.额外的关键字 Groovy中比Java多一些关键字,不要将它们用于变量名称等. as def in trait 2.数组初始化 在Groovy中,{...}块保留用于闭包.这意味着您无法使用以下语法创建数组字面量: // java初始化 int[] array = { 1

python语法区别

python语法区别: 大小写敏感 (动态语言:python)变量不用声明 p.s: 静态语言(Java)必须声明变量 语句末尾可以不打分号 可以直接进行数学计算 复制.粘贴功能失效,粘贴到别的地方的代码需要重新检查缩进是否正确 整数没有大小限制 浮点数也没有大小限制,但超出一定范围直接表示为inf(无限大) 原文地址:https://www.cnblogs.com/musecho/p/11666538.html

VB.NET与C# 语法区别展示

在学习VB.NET后发现,VB.NET与C#的语法主要的不同在两个部分,这两部分搞通了,那就游刃有余,迎刃而解了.现将其对比总结如下: 一.实体部分 (与VB相比,在C#和VB.NET中,实体的使用很有新意,将其作为第一个对比呈现) C#实体:语法格式为 //方式 public int ID { get { return ID; } set { ID = value; } } //方式 public int ID { get; set; } VB.NET实体:语法格式为 Private stuC

201671010139 2016-2017-2 JAVA 和C语言的语法区别

java和c语言的语法上有很多相似的地方,但也有很多不同. 一,在初始值的区别 在C语言中,是可以不初始化使用的 而在JAVA中,是必须初始化值的 二,在抽象方法或抽象类的区别 C语言的对等语法是"纯虚函数"和"抽象类" Java使用abstract关键字修饰抽象方法或抽象类,final类不能被继承 都使用抽象类作为继承层次中的基类,提供一般概念,由子类实现其抽象方法,且抽象类都不能被直接实例化为对象 E.super关键字的区别 JAVA super关键字,指代父类

C#和JAVA语法区别

< > <= >= instanceof(Java特有) 解释,当你拿到一个对象的引用时(例如参数),你可能需要判断这个引用真正指向的类.所以你需要从该类继承树的最底层开始,使用instanceof操作符判断,第一个结果为true的类即为引用真正指向的类. 在 Java 中,包是类.接口或其它包的集合,包主要用来将类组织起来成为组,从而对类进行管理. 没有像VS里的#region,#endregion一样的代码段区域 java中声明”属性”: //声明两个私有字段 private

python2 与 python3 语法区别

原文地址:http://old.sebug.net/paper/books/dive-into-python3/porting-code-to-python-3-with-2to3.html 使用2to3将代码移植到Python 3 ? Life is pleasant. Death is peaceful. It’s the transition that’s troublesome. ?— Isaac Asimov (attributed) 概述 几乎所有的Python 2程序都需要一些修改

sql查询一个面试题的7步解法及语法区别分析讲解

本篇讲述的是对一个sql面试题的细致语法讲解.关于执行流程(on where),内连接,外连接(左右)上实用.关于这些基本的语法知识请参考我前面的sql基本语法. 这张表的名字SC,SNO学号,CNO课程号,SCGRADE成绩.要求:列出“1”号课程成绩比“2”号课程成绩高的所有学生学号及其“1”号课程和“2”号课程的成绩 要求:列出“1”号课程成绩比“2”号课程成绩高的所有学生学号及其“1”号课程和“2”号课程的成绩 1,分别查出1 2号课程的所有列表(包括学号,课程,成绩). 2,条件1课程