X86 汇编/FASM 语法

FASM采用Intel语法, 符号名分大小写.

一个FASM源码文件由命令行, 标签行, 指示符行, 注释行组成. 注释可单独一行, 也可尾于其他行. 行尾符不分Windows或Linux. 行尾反斜杠'\'用于续行.

源码由词构成. 词用于各种命名. 组成词的字符分三种:

  • 单符: +-*/= <>() []{} :, |&~# ` 每个单独成词, 不与其它字符结合成词
  • 引符: 单引号和双引号. 成对使用内含单符或名符构成字串. 连续多个单引号或双引号视为一个
  • 名符: 单符和引符之外所有符号, 用于构词. 词首尾以空格或单符为界与他者分开.

命令语法 编辑

每个命令行含:

助记符 零个或多个操作数(Operand) ; 注释, 这是命令行的一般形式

这些操作数隔以逗号加零个或多个空格. 操作数可以是暂存器, 立即数或内存数, 前置以尺寸符来限定大小, 但暂存器名字蕴尺寸, 不可前置以尺寸符. 立即数可为任何算式. [内存地址, 可为任何算式] 表示取该地址内的数. 也可用 ptr 内存地址 的方式. 例

mov word [0x55aa55aa], 0x12345678
等价于
mov word ptr 0x55aa55aa, 0x12345678
尺寸符表格
尺寸符 位数 字节数
byte 8 1
word 16 2
dword 32 4
fword 48 6
pword 48 6
qword 64 8
tbyte 80 10
tword 80 10
dqword 128 16
xword 128 16
qqword 256 32
yword 256 32
暂存器表
位数
通用 8 al cl dl bl ah ch dh bh
16 ax cx dx bx sp bp si di
32 eax ecx edx ebx esp ebp esi edi
16 es cs ss ds fs gs
控制 32 cr0 cr2 cr3 cr4
除障 32 dr0 dr1 dr2 dr3 dr6 dr7
FPU 80 st0 st1 st2 st3 st4 st5 st6 st7
MMX 64 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7
SSE 128 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7
AVX 256 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7
AVX-512 512 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7
Opmask 64 k0 k1 k2 k3 k4 k5 k6 k7
128 bnd0 bnd1 bnd2 bnd3

内存格 编辑

CPU处理数据时, 暂存器容量有限, 大部分数据都是存在记忆体中. 那怎样存取记忆体中的数据呢? 可以想像记忆体中有各种尺寸的格子, 就叫内存格好了. 格之最小单元为一字节. 计算机非常灵活, 可以定义和预留任何尺寸的格. 常见格尺寸与暂存器尺寸一样, 方便数据交换. 大格常用于跟硬盘交换数据, 主要是文件.

1. 内存格的定义:

  • 定义单个或多个数据:
数据定义指示符 一个或多个算式 ;算式值即格之内容

db 0x55, 0xaa, 0x55 ;定义三个字节格值分别为0x55, 0xaa, 0x55
  • 定义字串:
数据定义指示符 任意长字串 ;字串即格之内容, 数据定义指示符db定义字节串; du定义词串, 每个词高字节为0
  • 定义长指针:
数据定义指示符 算式1:算式2 ;此处数据定义指示符为dp, df, dd, dt之一, 用于定义指针.

dp或df 1:2  ;定义之长指针高字=式1, 低双字=式2
dd 1:2  ;定义之长指针高字=式1, 低字=式2
dt 1:2  ;定义之长指针高字=式1, 低四字=式2

当dt只带一个算式时, 此算式值用于FPU扩展双精度浮点值.

  • 定义多个备份:
数据定义指示符 备份数 dup 一个或(多个算式, 算式间隔以逗号) ;定义多个备份, 定义<备份数>个<一个或(多个算式, 算式间隔以逗号)>备份.
  • 从文档中取内容:
file '档名'{:档内偏移, 默认档首},{要取字节数, 默认取至档尾} ;算式值即格之内容, 花括号参数可选

file '雪山飞狐'  ;取文档'雪山飞狐'全部内容
file '雪山飞狐':100h, 1024  ;从'雪山飞狐'偏移100h处往后取1024字节

2. 内存格预留:

数据预留指示符 格尺寸; 格尺寸可为任何算式

数据定义指示符和数据预留指示符统称数据指示符. FASM中的数据指示符见下表.

数据指示符
尺寸/字节 定义符 预留符
1 db,file rb
2 dw,du rw
4 dd rd
6 dp,df rp,rf
8 dq rq
10 dt rt

值, 恒量, 标签, 特殊符号与算式 编辑

算式 编辑

算式可以是单独的值, 恒量, 标签, 也可以是值, 恒量, 标签, 算符(算术符和位算符)和括号构成的算数表达式. 算式值在源码编译时确定, 靶容器溢出或超出FASM能力会有编译错误提示. 算符有不同的优先级, 可被括号改变. 在下面的算符优先级表中, 数值大优先级高.

算符优先级
算符 优先级
+ - 0
* / 1
mod 2
and or xor 3
shl shr 4
not 5
bsf bsr 6
rva plt 7

表中算符含义:

  • +,-,*,/为标准算术符, mod为取余
  • and, or, xor, shl, shr, bsf, bsr 和 not为位算符, 操作同同名汇编指令
  • rva和plt为特殊单目算符, 用于各类地址转换, 限用于极少汇编输出程序中, 具体操作与上下文相关.

编辑

  • 整数默认为十进制表示. 二进制后缀以b, 八进制后缀以o, 十六进制后缀以h或前缀以0x(C语言风格)或$(Pascal风格, 不建议使用)
  • 算式中的字串被转换成整数, 权序从左至右升高, 与日常书写顺序相反.
  • FASM不支持编译时进行浮点数运算, 算式中只能含单独的单精度浮点数, 后缀以f, 或含小数点, 或书以科学计数法. 如2f, 2.0, 2E0表示同一浮点数, 而2是整数.

特殊符号 编辑

4个可用于算式中的特殊符号:

  • $ 当前偏移
  • $$ 当前供数空间基址
  • % 循环源码当前循环次数
  • %t 编译时之时戳

恒量与标签 编辑

过多数值会造成程序难读. 对数值取名, 即得恒量与标签.

  • 定义恒量
恒量名=算式    ;可定义多次, 用式取其前面行中该恒量名最近一次定义. 若只定义一次, 可在其定义的源码文件中任何位置引用, 不分前后
例:
身高=165    ;
  • 定义标签. 标签只可定义一次, 可在其定义的源码文件中任何位置引用, 不分前后. 标签实为指针.

1. 码段中的标签用于标记码位:

标签名: {指令行}   ;标签名后缀以冒号即定义了该标签, 其值为所在码中此处地址, 用于标记码位. 花括号命令行可选.
例:
开始:    ;

2. 数段中的标签用于标记数值首地址:

标签名 数据指示符 一个算式或(多个算式隔以逗号)   ;标签名即该处算式编译后数值内存首址, 为指针名, 类型由数据指示符确定. 见例.

身高 db 165, 170, 181    ; 定义了标签身高, 身高存储的是165的地址, 乃字节指针
mov ax,身高              ; 地址搬入ax
mov al,[身高]            ; 165搬入al
mov al,ptr 身高          ; 165搬入al
mov ax,[身高]            ; 编译错, 源靶尺寸不匹配
mov ax,word [身高]       ; 165, 170搬入ax, al=165, ah=170

3. 用 label 指示符定义标签

label 标签名 {:} {尺寸符} {at} {算式, 其值乃标签之内容}    ; 最灵活的标签定义方式, 若无参数算式, 则标签值为当前地址

label 双高 word at 身高    ; '身高'为上例之标签, '双高'定义为始于'身高'地址的16位值(两个字节)的标签
mov ax, [双高]    ; 等价于 mov ax,word [身高]

4. 特殊标签:

  1. 局部标签(层次标签)
    1. 前缀以'.'(点)之标签名为局部标签, 前缀以其前面最近的全局标签构成此标签全名, 其简名可在下一个全局标签前随处使用
    2. 前缀以'..'(双点)之标签名为全局标签的引用, 不能用于局部标签的新前缀
  2. 无名标签
    1. @@ 无名标签, 数目不限
    2. @b,@r 引用前面最近的无名表情 (b-before, 前面)
    3. @f 引用后面最近的无名标签 (f-follow, 随后)

跳转与呼叫 编辑

跳转或呼叫 尺寸符 远近符 [地址算式]   ; 远近符: short, near, far
例:
jmp dword [0]   ; 16位模式中默认为远跳, 32位模式中默认为近跳, 可用远近符修改
jmp near dword [0]   ; 强制为近跳
jmp far dword [0] 强制为远跳

跳转或呼叫 尺寸符 立即数   ; 默认生成最短跳, 除非用远近符修改

尺寸设定 编辑

默认生成指令之规则为最小资源化, 除非有尺寸符明确指定.

指令 {尺寸符} [地址]   ; 无尺寸符时默认以最短位移生成最小指令
指令 操作数, {尺寸符} 立即数    ; 这里指令为adc, add, and, cmp, or, sbb, sub 和 xor, 无尺寸符时, 若立即数可表为8位有符号数, 则16-位或32-位操作数默认生成为8位. imul指令尾操作数为立即数时遵循同样规则
push {尺寸符} 立即数    ; 无尺寸符时, 此立即数于16位模式中当作词, 32位模式中作双词, 若可行则生成8位式指令. 尺寸符word或dword强制生成push长式指令
pushw或pushd强制fasm生成16位或32位码而非长式指令.