现在位置: 首页 > 汇编语言 > 正文

汇编语言 - 基础语法

本章介绍 NASM 汇编程序的基本结构、语法规则和书写规范,帮助你理解汇编代码的骨架。


汇编程序的基本结构

一个完整的 NASM 汇编程序通常由以下几个部分组成:

实例

; 文件路径:structure.asm
; NASM 程序基本结构示例

section .data                       ; 数据段:存放已初始化的数据
    ; 这里定义变量和常量
    msg db 'Hello, RUNOOB!', 0xA
    len equ $ - msg

section .bss                        ; BSS 段:存放未初始化的数据
    ; 这里预留内存空间
    buffer resb 64                  ; 预留 64 字节的缓冲区

section .text                       ; 代码段:存放可执行指令
    global _start

_start:
    ; 这里写程序逻辑
    mov eax, 4
    mov ebx, 1
    mov ecx, msg
    mov edx, len
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80
段(Section)用途特点
.data存放已初始化的全局变量和常量编译时确定大小和内容,存入可执行文件
.bss存放未初始化的全局变量只在运行时分配空间,不占用可执行文件大小
.text存放可执行的机器指令只读,包含程序的全部逻辑代码

至少需要 .text 段才能构成一个有效的汇编程序。如果没有数据,可以省略 .data.bss 段。


汇编语句格式

每条汇编语句的通用格式为:

[标签:]   指令助记符   [操作数1 [, 操作数2 [, 操作数3]]]   [; 注释]

各部分说明:

部分是否必须说明
标签(Label)可选代表一个内存地址的符号名,以冒号结尾
指令助记符必须如 mov、add、sub 等,告诉 CPU 要做什么
操作数可选(部分指令无操作数)指令操作的数据对象,可为寄存器、内存地址、立即数
注释可选以分号开头,一直延续到行尾

实例

; 各种语句格式示例

; 只有指令,无操作数
ret                     ; 从子程序返回

; 指令 + 单个操作数
push eax                ; 将 eax 的值压入栈中
inc ecx                 ; ecx 加 1

; 指令 + 两个操作数(最常见)
mov eax, 42             ; 将 42 复制到 eax 寄存器
add ebx, ecx            ; ebx = ebx + ecx

; 带有标签
loop_start:             ; 标签:标记循环开始位置
    dec ecx             ; ecx 减 1
    jnz loop_start      ; 如果 ecx 不为 0,跳回 loop_start

注释规范

NASM 使用 分号(;) 表示注释,从分号到行尾的内容都会被汇编器忽略。

实例

; 整行注释:说明下面代码块的用途
; 计算两个数的和并输出结果

mov eax, 10             ; 行内注释:将 10 放入 eax
add eax, 20             ; 行内注释:将 20 加到 eax,现在 eax = 30

汇编代码中注释极其重要。没有注释的汇编代码过几周连作者自己都可能看不懂。养成每条指令都写注释的习惯。


标识符命名规则

标识符(标签、变量名、常量名等)须遵循以下规则:

规则说明
组成字符字母、数字、下划线 _、点 .、问号 ?、@、$、# 等
起始字符必须以字母、下划线、点或问号开头,不能以数字开头
大小写默认区分大小写(可通过编译选项修改)
保留字不能和指令助记符、寄存器名或 NASM 关键字重名

实例

; 合法的标识符
my_variable:            ; 字母开头 + 下划线
.loop_start:            ; 点开头(局部标签)
?error_handler:         ; 问号开头
counter2:               ; 字母 + 数字

; 不合法的标识符(仅供参考,不要使用)
; 1st_value:            ; 错误:不能以数字开头
; mov:                  ; 错误:mov 是保留字
; my-variable:          ; 错误:减号不是合法字符

伪指令(Directives)

伪指令 是给汇编器的命令,不是给 CPU 的指令,用于控制汇编过程和定义数据结构。

伪指令用途示例
db定义字节(1 字节)byte_val db 0x55
dw定义字(2 字节)word_val dw 0x1234
dd定义双字(4 字节)dword_val dd 0x12345678
equ定义常量MAX_SIZE equ 256
resb预留字节空间buffer resb 128
resw预留字空间wbuf resw 64
resd预留双字空间dbuf resd 32
%define宏定义常量%define COUNT 10

大小写规范

NASM 默认对标签和标识符 区分大小写

实例

; 大小写敏感的示例

section .data
    msg db 'RUNOOB', 0       ; 定义变量 msg

section .text
    global _start

_start:
    mov eax, MSG             ; 错误:MSG 和 msg 不同(除非开启忽略大小写)
    mov eax, msg             ; 正确:msg 与定义完全一致

    MOV EAX, 42              ; 语法正确:指令助记符不区分大小写
    mov eax, 42              ; 推荐写法:用小写,可读性更好

指令助记符和寄存器名不区分大小写(MOVMovmov 效果相同),但推荐的风格是统一小写。


数值表示方式

NASM 支持多种进制的数值表示:

实例

; NASM 中不同进制的表示方式

mov eax, 42             ; 十进制:直接写数字
mov eax, 0x2A           ; 十六进制:0x 前缀(推荐写法)
mov eax, 2Ah            ; 十六进制:h 后缀
mov eax, 0o52           ; 八进制:0o 前缀
mov eax, 52o            ; 八进制:o 后缀
mov eax, 101010b        ; 二进制:b 后缀
mov eax, 0b101010       ; 二进制:0b 前缀

推荐使用 0x 前缀表示十六进制(如 0x2A),这样不容易和标签混淆。


一个完整的语法示例

下面程序综合运用以上语法元素,计算 1 到 10 的和并输出:

实例

; 文件路径:sum.asm
; 计算 1+2+...+10 并输出结果字符

section .data
    result db 0                 ; 存放计算结果(1字节)
    newline db 0xA              ; 换行符

section .text
    global _start

_start:
    ; 初始化寄存器和变量
    mov ecx, 10                 ; 循环计数器:从 10 开始倒数
    mov eax, 0                  ; eax 存放累加和,初始为 0

sum_loop:                       ; 循环开始标签
    add eax, ecx                ; eax = eax + ecx
    dec ecx                     ; ecx 减 1
    jnz sum_loop                ; 如果 ecx != 0,继续循环

    ; 此时 eax = 55(10+9+...+1)
    add eax, '0'                ; 将数字转为 ASCII 字符('0'=48,55+48=103='g',不对)
                                ; 实际演示需要更复杂的转换,见后续章节

    ; 这里只输出 result(简化演示)
    mov [result], al            ; 将累加结果存入 result

    ; 退出程序
    mov eax, 1
    mov ebx, 0
    int 0x80

注意:上面的示例中,直接加 '0' 只在数字是 0-9 范围内正确。处理两位数及以上的数字转换,将在后续章节详细讲解。