Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/gzyangkui/assembly

Assembly personal study note
https://github.com/gzyangkui/assembly

Last synced: 3 days ago
JSON representation

Assembly personal study note

Awesome Lists containing this project

README

        

## 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代码段中申明魏初始化数据。

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

## 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位数