GZYangKui / assembly

Assembly personal study note

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The NASM Language

  • NASM源码布局 与大多数汇编语言一样,NASM源码行包括一下四种字段的组合(宏、预处理指令、汇编指令除外)。

label: instruct operands ;comment

  • 编写规范

    • NASM使用作为行继续符,如果一行以反斜杠结束,则下一行被看做这一行的一部分,而不是独立的一行。
    • NASM并不限制在一行中的任何位置添加空格。
    • 标识符中的有效字符包括字母、数字、u、$、#、@、~、。,且标识符仅能以字母、下划线(_)和问号(?)开头。
    • 如果标识符和汇编保留字段冲突,如果需要访问可以使用$引用标识符,比如有个标识符命为eax,该标识符与eax寄存器冲突,如果需要访问该标识符可使用$eax访问。
  • NASM伪指令

    • DB AND Friends:定义初始化数据
    指令 大小
    db 1B
    dw 2B
    dd 4B
    dq 8B
    dt 16B
    • RESB and Friends:定义未初始化数据

      RESB, RESW, RESD, RESQ, REST, RESDQ, and RESO用于在BSS代码段中申明魏初始化数据。

    例子:

     buffer: resb 64 ; 保留64字节
     wordvar: resw 1 ; 保留一个字
     realarray resq 10 ; 保留80个字节
    • 引入外部二进制文件 INCBIN在输出文件中逐字包含一个二进制文件。这对于(例如)包括图形和声音数据直接输入游戏可执行文件。但是,建议将其用于只有一小部分数据。它可以通过以下三种方式之一调用:
    incbin "file.dat" ; 包含整个文件
    incbin "file.dat",1024 ; 跳过文件开头1024个字节
    incbin "file.dat",1024,512 ; 跳过文件头前1024个字节,并读取512个字节
    • 常量定义(equ) equ定义一个常量,equ前必须包含一个label,常量值一旦定义就无法更改。
    message db ’hello, world’
    msglen equ $-message
    • times:指令/数据重复 times会将后面的指令重复指定的次数:
    zerobuf:        times 64 db 0;

    times中的次数除了可以是常量,还可以是表达式:

    buffer: db      ’hello, world’        
    times 64-$+buffer db ’ ’ ;补齐64字节

    由于NASM在处理了macro之后才会处理times伪指令,因此需要注意是是times伪指令不能在macro里面使用。

NASM预处理器

  • 单行的宏
 %define label instruct

注意%define是大小写敏感的,在代码%define foo bar之后,只有‘foo‘会被扩展成‘bar’,‘Foo’和'FOO 都不会。用%idefine可以一次性定义所有大小写不停的宏。所以%idefine foo bar会导致'foo'、'FOO','Foo'等都会扩展成bar。

寻址方式

  • 什么是寻址方式

程序是指令的集合,指令包括操作码和操作数。寻址可分为指令的寻址即表示下一条指令的位置,还有便是数据的寻址即操作数的寻址,只有找到这个操作数才能对数据进行操作。

  • 内存地址引用通用格式

地址或偏移 (%基址寄存器,%索引寄存器,比例因子)

所有字段均可选。要计算内存地址,你只需执行以下计算:

结果地址=地址或偏移+%基址或偏移量寄存器+比例因子*%索引寄存器

地址或偏移以及比例因子必须是常量,基于两个必须是寄存器,如果省略其中任意一项,那么灯饰中将以0代替该项。

  • 直接寻址方式
  • 直接寻址方式 此模式通过使用地址或偏移量实现。示例:

movl ADDRESS,%eax

以上指令将内存地址ADDRESS加载到%eax寄存器中

  • 索引寻址方式 这种模式使用地址或偏移以及%索引寄存器部分实现。你可以将任何通用寄存器作为索引寄存器, 也可以将索寄存器的比例因子设置为1、2、4使之更适合为字节、双字节、和字进行索引。例如, 我们有一个名为string_start的字符串,并想访问其中第三个字节(由于从0开始,索引为2), %ecx中保存值为2,如果想将其加载到%eax中,可以通过如下指令实现:

movl string_start(,%ecx,1)

该指令从string_start处开始,将该地址与1*%ecx相加,并将所得值加载到%eax中

  • 间接寻址 间接寻址从寄存器指定地址加载值。例如,如果%eax保存一个地址,我们可以通过一下操作将该地址中的值移入%ebp:

movl (%eax),%ebp

  • 基址寻址方式 基址寻址方式与间接选址方式类似,不同之处在于它将一个常量与寄存器中的地址相加。例如,你有一个记录, 其中年龄段位于记录起始地址后4字节处,该记录的起始地址在%eax中,那么可以通过发出一下指令将年龄提取到%ebx中:

movl 4(%eax),%ebx

  • 立即选址方式 立即寻址方式十分简单。它与我们使用的通用格式有所不同。立即寻址方式用于直接将值加载到寄存器或者存储器位置。 例如想加载12到寄存器%eax,只需执行以下指令:

mov $12,%eax

注意为了表明立即寻址方式,我们在数字前加了一个$符号,若非如此就会变成直接寻址方式,此时会将位于存储位置12中的值 而不是12本身加载到寄存器%eax

多源码文件

  • 如果一个函数/变量在当前源码文件中不存在,汇编编译器在编译时将会产生错误, 为了通知汇编编译器该函数/变量定义在其他文件中,可以使用extern语句。

语法:extern <synmbolName>

Q&A

  • CDQ指令实现原理?

CDQ 是一个让很多人感到困惑的指令。 这个指令把 EAX 的第 31 bit 复制到 EDX 的每一个 bit 上。 它大多出现在除法运算之前。它实际的作用只是把EDX的所有位都设成EAX最高位的值。也就是说,当EAX <80000000, EDX 为00000000;当EAX >= 80000000, EDX 则为FFFFFFFF。 例如 : 假设 EAX 是 FFFFFFFB (-5) ,它的第 31 bit (最左边) 是 1, 执行 CDQ 后, CDQ 把第 31 bit 复制至 EDX 所有 bit EDX 变成 FFFFFFFF 这时候, EDX:EAX 变成 FFFFFFFF FFFFFFFB ,它是一个 64 bit 的大型数字,数值依旧是 -5。

备注: EDX:EAX,这里表示EDX,EAX连用表示64位数

About

Assembly personal study note

License:Apache License 2.0


Languages

Language:Assembly 85.2%Language:Shell 14.8%