第三章 汇编语言基础

Google TranslateGTranslateWinS Options

3.1 汇编语言的基本元素

3.1.1 整数常量

  整数常量由符号(可选)开头,加上一个或多个数字及一个表示数制基数的字符构成:

  

[{+|-}] digits [radix]

  本章全部使用微软的语法格式符号,[...]中的参数可选,{..}内参数要求从多个括起的采纳数中选择一个(由|分隔)。

  Radix(基数后缀)可为下列之一(不区分大小写):

h     十六进制
q/o  八进制
d    十进制
b    二进制
r    编码实数
t    十进制(可选)
y    二进制(可选)

  若整数常量后无后缀,则默认为十进制,以字母开头的十六进制必须加0,以防止汇编编译器将其解释为标识符。

3.1.2 整数表达式

  整数表达式是包含整数值和算术运算符的数学表达式,整数表达式计算的结果必须是能够存储在32位整数内的。如下:

运算符         名称          优先级
  ()           圆括号          1
 +, -          单目加、减       2
 *, /          乘、除          3
 MOD           取模            4
 +, -          加、减          5

3.1.3 实数常量

  有两种类型的实数常量:十进制实数和编码(十六进制)实数。

  十进制实数由符号位、整数部分、小数点、表示小数的整数和指数部分组成:

[sign]integer.[integer][exponent]

  符号位和指数的描述如下:

sign         {+, -}
exponent  E[{+, -}]integer

  实数常量中应该至少有一个数字和一个小数点。

  编码实数 如果准确的知道数字的二进制表示,就可以使用十六进制指定编码实数常量。(IEEE实数格式的讨论在第17章)

3.1.4 字符常量

  字符常量时以单引号或双引号引起的单个字符。汇编编译器将其转换为与字符对应的二进制ASCII码。

3.1.5 字符串常量

  字符串常量以单引号或双引号引起的一串字符:

'ABC'
'X'
"Goodnight, Gracie"
'4096'

'Say "Goodnight, " Gracie'

3.1.6 保留字

  保留字可以是下列之一:

  * 指令助记符, 如 MOV、ADD、MUL

  * 伪指令, 用于告诉MASM如何编译

  * 属性, 用于为变量和操作数提供有关尺寸以及使用方式的信息, 如 BYTE和WORD

  * 运算符, 用在常量表达式中

  * 预定义符号, 如@data, 在编译时返回整数常量值

  完整的MASM保留字见附录D

3.1.7 标识符

  标识符是程序员使用的名字,用来识别变量、常量、过程或代码标号。

  * 可包含1-247个字符

  * 大小写不敏感

  * 第一个字符必须是字母、下划线、@或$, 后续字符可为数字

  * 标识符不能与汇编编译器的保留字相同

  运行汇编编译器时, 通过在命令行加-Cp可使关键字和标识符大小写敏感。

  尽量避免使用@开头的标识符,因为@符号被编译器扩展用于预定义的符号。

3.1.8 伪指令

  伪指令时程序源代码被编译时由编译器识别和执行的命令, 不同编译器不同。如:

  .DATA 伪指令标识了程序中包含变量的区域

  .CODE 伪指令标识了程序中包含指令的区域

  PROC 伪指令标识过程的开始

  附录D包含MASM伪指令和运算符的完整参考手册。

3.1.9 指令

  指令是在程序被加载至内存并且开始运行后, 在运行期间由处理器执行的语句, 一条语句包含四个基本部分:

  * 标号(可选)

  * 指令助记符(必须)

  * 操作数(通常需要)

  * 注释(可选)

  源代码行可以只包含标号和注释, 下面显示了指令的标准格式:

 

标号: 指令助记符 操作数 ; 注释

 

3.1.9.1 标号

  标号是充当指令或数据位置标记的标识符。

  代码标号 程序代码区中的标号必须以冒号(:)结尾, 在此位置, 代码标号通常用作跳转和循环指令的目标地址。代码标号可以和指令在同一行, 也可以独自成行。

  数据标号 不必使用冒号(:)结尾。

3.1.9.2 指令助记符、操作数、注释

  注释可使用以下两种方法指定:

  * 单行注释: 以分号(;)开始, 之后所有字符视为注释

  * 块注释: 以COMMENT 伪指令及一个用户自定义的符号开始, 之后直到再次相同自定义符号出现之间的字符视为注释。

 

 

3.2 例子: 三个整数相加

3.2.1 程序清单

TITLE Add and Subtract     (Add.asm)
; This program ads and substracts 32-bit integers
INCLUDE Irvine32.inc
.code
main PROC
    mov eax, 10000H
    add eax, 40000H
    sub eax, 20000H
    call DumpRegs
    exit
main ENDP
END main

 

3.2.3 程序说明

  TITLE 伪指令将整行标为注释。

  ;将之后的整行字符标为注释。

  INCLUDE 伪指令从Irvine32.inc文件中拷贝必须的定义和设置信息。

  .code 伪指令用来标记代码段的开始

  main PROC: PROC伪指令用来标识子程序的开始, 名称为main

  call DumpRegs: CALL指令调用一个显示CPU寄存器值得子程序。

  exit 表达式(间接)调用预定义的MS-Windows函数来终止程序。

  main ENDP: ENDP伪指令表明该行时汇编源程序的最后一行, 编译器将忽略该行之后又的所有内容, 其后的标识符main是程序启动过程(程序启动时执行的子程序)的名字。

  程序是以段组织的, 常见的段有代码段。数据段和堆栈段等。代码段包含程序的全部可执行指令, 通常代码段中都有一个或一个以上的过程, 其中一个是启动过程。堆栈段存放着子程序的参数和局部变量, 数据段则存放着变量。

3.2.3.1 AddSub的另一个版本

  上一个程序隐藏了许多细节。

 

TITLE Add and Substract
.386
.MODEL flat, stdcall
.STACK 4096
ExitProcess PROTO, dwExitCode:DWORD
DumpRegs PROTO

.code
main PROC
    mov eax, 10000H
    add eax, 40000H
    sub eax, 20000H
    call DumpRegs

    INVOKE ExitProcess, 0
main ENDP
END main

 

  .386指出来该程序要求的最低CPU(Intel 386)

  .MODEL flat, stdcall: 为保护模式生成代码, STDCALL允许调用MS-WIndows函数。

  PROTO伪指令声明了该程序使用的子程序。

  INVOKE 是一个用于调用过程或函数的汇编伪指令。

 

3.4 定义数据

3.4.1 内部数据类型

 3.4.2 数据定义语句

  数据定义语句为变量在内存中保留存储空间并且可以为变量指定名称。

[名称] 数据定义伪指令 初始值 [, 初始值 ...]

  初始值 数据定义语句中至少要有一个初始值或"?"符号, "?"在不想给数据赋予任何特定值的时候使用。

2.4.3 定义BYTE、SBYTE、WORD、SWORD、DWORD、SWORD/.QWORD、TBYTE、实数数据

  BYTE(无符号字节)和SBYTE(有符号字节)伪指令为一个或多个有符号及无符号字节分配空间, 每个初始值必须是8位的整数表达式或字符常量。

value1 BYTE 'A'           ; 字符常量
value2 BYTE 0             ; 最小无符号字节常量
value3 BYTE 255           ; 最大无符号字节常量
value4 SBYTE -128         ; 最小有符号字节常量
value5 SBYTE +127         ; 最大有符号字节常量
value6 BYTE ?
value7 DB 255
value8 DB -128
list1 BYTE 10, 20, 30, 40 ; 偏移从左到右增大
list2 BYTE 10, 20, 30, 40
BYTE 60, 70, 80, 90
BYTE 11, 22, 33, 44
list3 BYTE 10, 20, 41H, 00100010b
list4 BYTE 0AH, 20H, 'A', 22H
string1 BYTE 'Hello, World !', 0
BYTE "send me a message", 0DH, 0AH, 0 ; 回车换行
string2 \
BYTE 0AH, 20H, 'A', 22H

   变量名 变量名是一个标号, 表示变量相对于其所在段开始的偏移, value2的偏移值为1

 DUP操作符 DUP操作符使用一个常量表达式作为计数器来重复分配空间

BYTE 20 DUP(0)        ; 20字节, 全部初始化为0
BYTE 20 DUP(?)         ; 20字节, 未初始化
BYTE 4  DUP("STACK") ; 20字节, "STACKSTACKSTACKSTACK

  WORD(字)、SWORD(有符号字)分配16位存储空间, 老版本编译器使用DW。

  DWORD(双字)、SDWORD(有符号双字)分配32位存储空间, DD

  QWORD(四节)分配8字节空间, DQ

  TBYTE(五字)分配10字节空间, DT

  REAL4定义4字节单精度实数

  REAL8定义8字节双精度实数

  REAL10定义扩展精度实数

3.4.9 小尾顺序

  Intel处理器使用小尾顺序(little endian order)的方案存取内存数据, 其含义即变量的最低有效字节存储在地址最小的地址单元中, 其余字节按顺序存储。

  双字12345678H(305419896D)的存储:

3.4.10 位AddSub程序添加变量

TITLE Add and Substract           (AddSub.asm)
; This programs adds and substracts 32-bit integers.
INCLUDE Irvine32.inc

.data
val1 DWORD 10000H
val2 DWORD 40000H
val3 DWORD 20000H
finalVal DWORD ?

.code
main PROC
    mov eax, val1    ; 注意得到的不是偏移量
    add eax, val2
    sub eax, val3
    mov finalVal, eax
    call DumpRegs
    
    exit
main ENDP
END main

3.4.11 未初始化数据的声明

  ".DATA?"伪指令可用于声明未初始化的数据, "DATA?"在定义大块的未初始化数据时非常有用, 因为它可以缩小编译后的程序尺寸:

.data
smallArray DWORD 10 DUP(0)      ; 40bytes
.data?
bigArray DWORD 5000 DUP(?)      ; 2000bytes

  下面的代码将生成大于20000字节的程序:

.data
smallArray DWORD 10 DUP(0)
bigArray DWORD 5000 DUP(?)

  混合代码和数据 汇编编译器允许程序在代码和数据之间来回切换, 在定义仅在局部程序中使用的变量非常方便:

.code
mov eax, ebx
.data
temp DWORD ?
mov eax, temp

  编译器将temp和其他变量一起放到了数据段中, 对于同一代码文件中的每条指令来说, temp变量都可见。

 

 

3.5 符号常量

  符号常量(符号定义)通过将标识符与整数表达式或文本联系起来而创建的, 不占用存储空间, 仅在编译时使用, 不可在运行期间改变, 类似C语言的预定义常量(#define)。

3.5.1 等号伪指令

  等号伪指令将符号名和整数表达式联系起来:

名字 = 表达式

  如:

COUNT= 500
mov al, COUNT

; 编译器编译生成

mov al, 500

  也可使用在DUP中作为定义的数据数量。

  以"="重复定义定义的符号可重定义多次, 按照源代码的书写顺序由编译器改变其值。

2.5.2 计算数组和字符串的大小

  MASM使用$运算符(当前地址计数器)返回当前程序语句的地址偏移:

 

list BYTE 10, 20, 30, 40
listSize = ($ - list)

 

  字数组需要 / 2, 双字等数组类似

list WORD 1000H, 2000H, 3000H, 4000H
listSize = ($ - list) / 2

 

3.5.3 EQU伪指令

  EQU伪指令将符号名和整数表达式或任意文本联系起来:

name EQU expression           ; expression必须是有效的整数表达式
name EQU sybol                  ; sybol必须是已经使用"="或EQU定义的符号名
name EQU <text>                ; text字符串必须置于尖括号内

PI EQU <3.14159>
pressKey EQU <"Press any key to continue ...", 0>

.data
prompt BYTE pressKey

 

matrix1 EQU 10 * 10
matrix2 EQU <10 * 10>

.data
M1 WORD matrix1
M2 WORD matrix2

; 编译器处理为

M1 WORD 100
M2 WORD 10 * 10

  不允许重定义 与"="伪指令不同, 使用EQU定义的符号不能再同一源码文件中重复定义

2.5.4 TEXTEQU伪指令

  TEXTEQU与EQU非常相似, 可用于创建文本宏(text macro):

name TEXTEQU <text>               ; 将文本赋予符号
name TEXTEQU textmacro           ; 将已定义的文本宏内容赋予符号
name TEXTEQU %constExpr         ; 将整数表达式常量赋予符号
rowSize = 5
count TEXTEQU %(rowSize * 2)
move TEXTEQU <mov>
setupAL TEXTEQU <move al, count>

 TEXTEQU可在程序中重复定义。

 

3.6 实模式

3.6.1 基本修改

  将本章32位程序转换为16位程序, 只需做以下修改:

   * INCLUDE指令引用不同的库文件: INCLUDE Irvine16.inc

   * 启动过程开始必须插入两条指令, 初始化DS寄存器: mov ax, @data    mov ds, ax

   * 使用make16.dat批处理文件

   * 数据标号和代码标号的偏移为16位而非32位

TITLE Add and Substract           (AddSub.asm)
; This programs adds and substracts 32-bit integers.
INCLUDE Irvine16.inc

.data
val1 DWORD 10000H
val2 DWORD 40000H
val3 DWORD 20000H
finalVal DWORD ?

.code
main PROC
        mov ax, @data
        mov ds, ax
    mov eax, val1
    add eax, val2
    sub eax, val3
    mov finalVal, eax
    call DumpRegs
    
    exit
main ENDP
END main

 

 

 

 

posted @ 2019-07-23 20:27  YangDanMua  阅读(673)  评论(0编辑  收藏  举报