8086汇编(16位汇编)学习笔记09.宏汇编
宏汇编在文件中是当做关键字的,但是在bug中运行时并没有这些指令,这些关键词被称为伪指令,cpu并不认识他们,需要经过编译器转化成 cpu认识的代码,但是他多我们写代码帮助又很大表达式
表达式中的求值是在程序链接时完成的,所以表达式中的各值必须是在汇编或链接期就能确定,也就是 说不能将寄存器或者变量运⽤于表达式。
算术表达式
| 运算符 | 意义 | 例⼦ |
| -------- | ------ | --------------- |
| + | 加 | 65 + 32 |
| - | 减 | size val - 54 |
| * | 乘 | 23h * 65h |
| / | 除 | 98 / 45 |
| mod | 取模 | 99 mod 65 |
```
mov ax, 54+65
mov ax, 65-48
mov ax, 65*78
mov ax, type byte * 78
mov ax, 78 mod 5
mov ax, 96 / 5
mov ax, size g_buf / type g_buf
;mov ax,bx+7 ;这个是错的,因为编译器无法得到结果,不知道bx的值
```
!(./notesimg/1652871699132-34b2446a-9e0a-4c15-999d-b4059f2db368.png)
逻辑运算符
逻辑运算即位运算,逻辑运算符与对应的指令助记符单词是相同的,当它们出现在操作码部分时是指 令,出现在操作数时是逻辑运算符
| 运算符 | 意义 |
| -------- | ---------- |
| and | 位与 |
| or | 位或 |
| not | 按位取反 |
| xor | 异或 |
```
mov ax, 5566h and 6655h
mov ax, 7788h or 8877h
mov ax, not 5566h
mov ax, 5566h xor 7788h
```
!(./notesimg/1652872024974-f92ffe0b-4146-4507-b22e-4b3a16b47269.png)
关系运算符
关系运算符的结果,如果结果为真,则所有位都置为1,即FFFF;否则所有位都置为0,即 .
| 运算符 | 英⽂ | 意义 |
| -------- | ----------------------- | ------------- |
| EQ | equal | 等于 == |
| NE | not equal | 不等于 != |
| GT | greater than | ⼤于 > |
| LT | less than | ⼩于 < |
| GE | greater than or equal | ⼤于等于 >= |
| LE | less than or equal | ⼩于等于 <= |
```
mov bx,88 eq 88 ;bx= 0FFFFh 因为结果为ture
mov bx,88 ne 88 ;bx= 0000h 因为结果为flase
mov bx,88 ge 88 ;bx= 0FFFFh 因为结果为ture
```
!(./notesimg/1652872630303-78c97344-e2c8-44e3-b315-4e1b41b06f2f.png)
### 标号
#### 匿名标号@@
- @@是匿名标号
- @b 向上查找最近的@@, b是back
- @f向下查找最近的@@ ,f是front
一般我们用 jmp 跳转你需要有标号,而且标号名必须唯一 ,但是如果 标号名是 @@ ,那么就可以重复,跳转用@b 或@f, @@的 跳转距离在段内是 没有限制的
!(./notesimg/1652873630277-df26cf73-fb96-4a53-b5f4-73a6eb555989.png)
#### 调整偏移量指令ORG
格式 ORG 偏移值
此指令后的下⼀个变量或指令从 偏移值 开始存放,即把一个变量或者指令放在指定的偏移内,可以提前,也可以延后
```
;这是栈段
stack segment stack
db 512 dup(0)
stackends
;这是数据段
data segment
g_buf db "hello world"
org20
g_buf1 db "hello world";表示 g_buf1 会从段偏移20的地方开始存放
org0
g_buf2 db "111111" ;表示 g_buf2 会从段偏移0的地方开始存放,回覆盖 g_buf的数据
data ends
;这里是代码
CODE segment
START:
assume ds:data
mov ax, data
mov ds, ax
org 20h
mov ax,4C00H; 带参数返回的退出 ;此处代码从20H 处开始存放,后面指令依次后推
int 21h ;使用21号中断
CODE ends
end START
```
!(./notesimg/1652874628115-dde37c51-74f2-47fd-93af-a113f6fd0952.png)
#### 当前地址指令$
- $伪指令代表当前指令或变量的地址
- 常⽤于计算缓冲区⻓度和获取当前ip值
- 可与 ORG 配合使⽤
```
data_seg segment
g_buf dw 10h dup(0)
g_len dw $ - offset g_buf;获取上⾯的g_buf的⻓度
org $ + 10h ;下⼀个变量从当前地址+10h的偏移开始存放
g_w dw 65h
data_seg ends
CODE segment
START:
org 100h
mov ax, 5566h and 6655h ;此指令从代码段偏移100h开始存放
mov ax, $ ;获取 本条指令的地址
mov ax, $+4;获取 本条指令的地址+4
mov ax, 4c00h
int 21h
CODE ends
```
### 结构体 struc
#### 格式
结构体名 struc
```
;这⾥定义结构体成员
```
结构体名 ends
结构体使⽤<>来初始化
结构体可以通过变量名和寄存器来访问成员
结构体可以在定义时候初始化,也可以在后面初始化
结构作为局部变量无法赋初值
```
;定义结构体
Point struc
m_wX dw 0
m_wY dw 0
Point ends
;关键字struc,可以在定义时候赋初值
TagStu struc
m_byte db 0
m_w dw 5656h
m_arydb 8 dup(0)
m_sz db "hello"
m_pt Point <> ;结构体里面嵌套结构体
TagStu ends
;堆栈
stack_seg segment stack
db 512 dup(0)
stack_seg ends
;数据段
data_seg segment
g_stu TagStu <66h, 7777h, "testtest", "66", <11h, 22h>> ;初始化结构体
g_ary Point 12 dup(<11h, 22h>) ;定义结构体数组
data_seg ends
;代码
code_seg segment
;结构体传参,直接传对象,它会将结构提对象入栈,每次入栈2字节(推荐用指针)
Foo0PROC far c stu:TagStu
lea bx, stu
mov stu.m_byte, 99h ;修改结构体成员的值
mov stu.m_w, 6666h ;修改结构体成员的值
ret
Foo0ENDP
;结构体传参,传对象指针,通过指针访问必须借助寄存器,不能直接用指针
Foo1 PROC far c pStu:ptr TagStu
;mov ax, m_w ;指针,不能直接点使用,必须通过寄存器
mov bx, pStu ;将结构体指针赋值给bx
;通过结构体指针访问结构体成员
assume bx:ptr TagStu ;将bx解释为 TagStu 结构体指针,类似强转
mov ax, .m_w ;通过结构体指针访问成员
assume bx:nothing ;将bx 的 解释方式转回默认
ret
Foo1 ENDP
;结构体作为局部变量,局部变量无法赋初值
foo PROC far c
local @stu:TagStu ;变量类型直接是结构体类型
lea bx, @stu ;获取变量的偏移地址
mov al, @stu.m_byte;访问变量成员
mov ax, @stu.m_w ;访问变量成员
ret
foo ENDP
START:
assume ds:data_seg
mov ax, data_seg
mov ds, ax
mov ax, g_stu.m_pt.m_wX ;访问结构体内 结构体的的成员
invoke Foo1, offset g_stu;结构体指针作为函数参数
invoke Foo0, g_stu ;结构体对象作为函数参数
invoke foo ;结构体作为局部变量
mov al, g_stu.m_byte ;访问结构体成员
mov ax, g_stu.m_w ;访问结构体成员
lea bx, g_stu.m_ary ;获取结构体成员的偏移地址
mov byte ptr , 'a' ;修改结构体成员的值
mov ax, 4c00h;
int 21h
code_seg ends
end START
```
!(./notesimg/1652878569539-52dfee2a-c7d8-4fd0-9146-cab4463410dc.png)
!(./notesimg/1652879157411-5d8c9bda-1f5a-4d52-a821-48bce6362bed.png)
### 宏
#### equ语句
作用:用来定义宏
不可以重命名
可⽤于常量和表达式
可⽤于字符串
可⽤于指令名,给指令取别名
可⽤于类型,给类型取别名
可⽤于操作数
```
;是无参宏,没有参数
;堆栈
stack_seg segment stack
db 512 dup(0)
stack_seg ends
PI equ 314 ;常量
SZNAME equ "hello world" ;字符串
MYMOV equ mov ;指令名
CHAR equ db ;类型
INT16 equ dw ;类型
;PI equ 6677h ;不允许再次定义
;数据段
data_seg segment
g_sz CHAR SZNAME
g_wINT16 PI
g_w2 INT16 44h
data_seg ends
;代码
code_seg segment
START:
assume ds:data_seg
mov ax, data_seg
mov ds, ax
MYMOV ax, g_w
mov ax, 4c00h;
int 21h
code_seg ends
end START
```
!(./notesimg/1652880751415-1447cb63-abe8-4e01-8647-fc36cd1ccadb.png)
#### =语句
可以被修改
只能⽤于常数
```
COUNT2 = 100h ;后跟数值
COUNT2 = 200h ;可以再次赋值
;szTest = "hello world";错误,不能用于字符串,只能用于立即数(常量)
mov ax, COUNT2
;可以用来提高可读性
MYFUNC:
nX = 2
nY = 4
push bp
mov bp, sp
sub sp, 10
mov , ax
mov , ax
mov sp, bp
pop bp
ret
```
#### macro语句
###### 格式
宏名 macro [参数1][,参数2]...
```
宏体
```
endm
宏会在使⽤的地⽅展开
宏可以带参数
字符串拼接使⽤&
```
;堆栈
stack_seg segment stack
db 512 dup(0)
stack_seg ends
CHAR equ db
INT16 equ dw
;内存到内存赋值宏
movm macro val1, val2
push val1
pop val2
endm
;字符串拼接
shift macro d, opt, count
push cx
mov cl, count
sa&d opt, cl
pop cx
endm
;数据段
data_seg segment
g_wINT16 55H
g_w2 INT16 44h
data_seg ends
;代码
code_seg segment
START:
assume ds:data_seg
mov ax, data_seg
mov ds, ax
;内存到内存赋值
movm g_w, g_w2
movm g_w2, g_w
shift r, ax, 2
shift r, ax, 5
shift l, bx, 4
shift l, dx, 2
mov ax, 4c00h;
int 21h
code_seg ends
end START
```
!(./notesimg/1652881928524-a25f6836-2120-4d09-8f82-ec2b2bb193ae.png)
!(./notesimg/1652882072991-f33eb32e-9d3d-4fcc-9a16-f3575787937e.png)
### 多文件编译
#### 1.源文件
- 源文件后缀名为asm
- 每个源文件末尾需要有end
#### 2.头文件
- 汇编头文件后缀名为inc
- 头文件包含include xxx.inc
- 头文件防重复包含
```
ifndef SECOND_1
SECOND_1 equ 1
Func1 proto far stdcall arg1 : word, arg2 : word
extern g_dw : word
endif
```
#### 3.函数使用
函数在源文件定义,在头文件中声明即可。
#### 4.全局变量
全局变量在定义文件中必须使用public指明此变量为全局public变量名
全局变量在使用文件中必须使用extern 指明此变量来自外部文件 extern 变量:类型
```
ifndef HEADER_INC ;防止重复包含
HEADER_INC equ 1
extern g_w:word;声明明 g_w 是来自外部的全局变量
endif
```
```
public g_w ;指名 g_w 是全局变量 ,不然默认本文件内才能使用(文件作用域)
data_seg segment
g_w dw 0
data_seg ends
end
```
#### 5.编译
```
ml /c xx.asm yy.asm
link xx.obj yy.obj
或
ml *.asm
```
### 伪指定实现分支,循环
!(./notesimg/1652882979464-f89aaf33-46e9-46bc-b334-306a3d6686bf.png)
#### 分支
格式
格式1:
.IF condition ;以英文“句号”开头 ;条件"condition"成立时所执行的指令序列
指令序列
.ENDIF
格式2:
.IF condition ;条件"condition"不成立时所执行的指令序列
指令序列1
.ELSE
指令序列2
.ENDIF
格式3:
.IF condition1
指令序列1
.ELSEIF condition2 ;条件"condition2"成立时所执行的指令序列
指令序列2
.ENDIF
其中:条件表达式“condition”的书写方式与C语言中条件表达式的书写方式相似,也可用括号来组成复杂的条件表达式。
条件表达式中可用的操作符有:==(等于)、!=(不等)、>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、&(位操作与)、!(逻辑非)、&&(逻辑与)、||(逻辑或)等。
```
;堆栈
stack_seg segment stack
db 512 dup(0)
stack_seg ends
;数据段
data_seg segment
data_seg ends
;代码
code_seg segment
START:
assume ds:data_seg
mov ax, data_seg
mov ds, ax
mov g_w, 8
.if ax >= bx
xor ax, ax
.elseif ax <= bx
xor bx, bx
.else
xor cx, cx
.endif
mov ax, 4c00h;
int 21h
code_seg ends
end START
```
1、WHILE型循环伪指令
.WHILEcondition ;条件"condition”成立时所执行的指令序列
循环体的指令序列
.ENDW
其中:.ENDW与前面的.WHILE相匹配,它标志着其循环体到此结束。
如果条件表达式“condition”在循环开始时,就为“假”(false),那么,该循环体一次也不会被执行。
2、REPEAT型循环伪指令
.REPEAT
循环体的指令序列
.UNTIL condition.REPEAT
循环体的指令序列
.UNTILCXZ
REPEAT型循环在执行完循环体后,才判定逻辑表达式condition的值。若该表达式的值为真,则终止该循环,并将执行伪指令.UNTIL后面的指令,否则,将向上跳转到伪指令.REPEAT之后的指令,为继续执行其循环体作准备
循环终止 break 和继续 continue
(1)、终止循环伪指令
.BREAK
.BREAK.IF condition
该伪指令用来终止包含它的最内层循环。前者是无条件终止循环,后者是仅当逻辑表达式condition为真时,才终止循环。
.WHILE1 .REPEAT
…
.BREAK .IF condition
…
…
.BREAK .IF condition
…
ENDW .UNTIL 0
对于以上二个循环,如果没有指令来终止循环的话,它们都将进入死循环状态,但如果在该层循环体内,存在伪指令“.BREAK .IF condition”的话,那么,当逻辑表达式condition为真时,该循环就会被终止了。
(2)、循环继续伪指令
.CONTINUE
.CONTINUE .IF condition
页:
[1]