- LOOP
- 格式:loop标号
- 只能用于短转移
函数
- 函数结构
- masm函数语法
- 我的SWAP函数: ```matlab assume cs:code,ds:data,ss:stack
data segment ;数据段 g_a word 1 g_b word 2 g_sz db “hello word$” ; g_buf db 255 dup(‘$’) data ends
stack segment stack db 128 dup (0) stack ends
code segment SWAP: ;先push两个参数 数据宽度为word ;保留调用前的栈底 push bp ;提升堆栈 mov bp, sp sub sp, 40h ;保留现场 push bx push si push di ;开始填充缓冲区 mov ax, 0cccch mov cx, 20h ; mov ax,sp mov es,ax ; lea ax,[bp-40h] mov di,ax rep stosw ;函数的核心功能 mov bx,ss:[bp+4] mov ax,ds:[bx] ;bp+2是变量的地址,指针数据 mov ss:[bp-2],ax ;将bp+2指向的数据保存在局部变量
mov bx,ss:[bp+6]
mov ax,ds:[bx]
mov bx,ss:[bp+4]
mov ds:[bx],ax
mov bx,ss:[bp+6]
mov ax,ss:[bp-2]
mov [bx],ax
;恢复现场
pop di
pop si
pop bx
;降低堆栈
mov sp, bp
pop bp
ret 4
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
;=============================code start:
;push 前,sp=80h,bp=00h
lea ax,offset g_a
push ax ;00h
lea ax,offset g_b
push ax ;02h
;call 前,sp=7ch,bp=00h
call SWAP
mov ax,[g_a]
mov bx,[g_b]
;=============================code end: mov ax,4C00H ;程序退出 int 21H code ends end start
两种平栈方式:ret imm(相当于pop ip ,sub sp,imm) 或 call 后面更 add sp,imm ;imm是根据入栈参数所占的word数来决定的
- 注意,下图功能只有push那行,没有写完整
![image.png](https://cdn.nlark.com/yuque/0/2021/png/12923347/1632930053853-8c03cead-ea25-4d45-bbdd-52f784f3651b.png#align=left&display=inline&height=523&margin=%5Bobject%20Object%5D&name=image.png&originHeight=523&originWidth=698&size=136582&status=done&style=none&width=698)
<a name="WL0IT"></a>
## masm函数语法
- 函数语法
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12923347/1632930657092-8c590699-6003-481e-8e70-e5e0c1e11eb0.png#align=left&display=inline&height=147&margin=%5Bobject%20Object%5D&name=image.png&originHeight=147&originWidth=673&size=33290&status=done&style=none&width=673)
- 示例
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12923347/1632930670588-0a4aabf2-8f0b-4b92-80ab-7ebbb5cbc652.png#align=left&display=inline&height=126&margin=%5Bobject%20Object%5D&name=image.png&originHeight=126&originWidth=667&size=23297&status=done&style=none&width=667)
- 简单示例(无函数功能)
```matlab
assume cs:code,ds:data,ss:stack
data segment ;数据段
g_a word 1
g_b word 2
g_sz db "hello word$" ;
g_buf db 255 dup('$')
data ends
stack segment stack
db 128 dup (0)
stack ends
code segment
Func PROC
ret
Func ENDP
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
;=============================code start:
call Func
;=============================code end:
mov ax,4C00H ;程序退出
int 21H
code ends
end start
- 距离
段内其实加不加near都是一样的,如同上面示例的Func
例:
assume cs:code,ds:data,ss:stack
data segment ;数据段
g_a word 1
g_b word 2
g_sz db "hello word$" ;
g_buf db 255 dup('$')
data ends
stack segment stack
db 128 dup (0)
stack ends
code_seg1 segment
Func1 PROC far
retf
Func1 ENDP
code_seg1 ends
code segment
Func PROC
ret
Func ENDP
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
;=============================code start:
call Func
call far ptr Func1
;=============================code end:
mov ax,4C00H ;程序退出
int 21H
code ends
end start
- 调用约定
- c调用约定外平栈,stdcall内平栈
示例:定义局部变量和使用调用约定以及寄存器环境
assume cs:code,ds:data,ss:stack
data segment ;数据段
g_wVal1 word 1
g_wVal2 word 2
g_sz db "hello word$" ;
g_buf db 255 dup('$')
data ends
stack segment stack
db 128 dup (0)
stack ends
code_seg1 segment
Func1 PROC far C
retf
Func1 ENDP
code_seg1 ends
code segment
;函数名 PROC 距离 调用约定 uses 寄存器环境 参数
Func PROC near stdcall uses bx cx si di pAddr1:word,pAddr2:word
;开始定义局部变量
;局部变量前加@是一种规范而不是语法要求,因为g开头和@开头用于区分局部变量和全局变量一目了然
local @btVal:byte
local @wVal:word
local @dwVal:dword
local @qwVal:qword
local @tbVal:tbyte
ret
Func ENDP
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
;=============================code start:
lea ax,offset g_wVal1
push ax
lea ax,offset g_wVal2
push ax
call Func
call far ptr Func1
;=============================code end:
mov ax,4C00H ;程序退出
int 21H
code ends
end start
- 然后是使用局部变量和参数 ```matlab assume cs:code,ds:data,ss:stack
data segment ;数据段 g_wVal1 word 1 g_wVal2 word 2 g_sz db “hello word$” ; g_buf db 255 dup(‘$’) data ends
stack segment stack db 128 dup (0) stack ends
code_seg1 segment
Func1 PROC far C retf Func1 ENDP
code_seg1 ends
code segment ;函数名 PROC 距离 调用约定 uses 寄存器环境 参数 Func PROC near stdcall uses bx cx si di pAddr1:word,pAddr2:word ;开始定义局部变量 ;局部变量前加@是一种规范而不是语法要求,因为g开头和@开头用于区分局部变量和全局变量一目了然 local @btVal:byte local @wVal:word local @dwVal:dword local @qwVal:qword local @tbVal:tbyte
;使用参数 mov si,pAddr1 mov di,pAddr2 mov ax,[si] mov bx,[di] mov [si],bx mov [di],ax
mov @btVal,1
mov @wVal,2
;下面三个变量不能这样直接赋值,因为8086是16位的,一次操作宽度不能过大
;mov @dwVal,3
;mov @qwVal,4
;mov @tbVal,5
ret
Func ENDP
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
;=============================code start:
lea ax,offset g_wVal1
push ax
lea ax,offset g_wVal2
push ax
call Func
call far ptr Func1
;=============================code end: mov ax,4C00H ;程序退出 int 21H code ends end start
- invoke伪指令
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12923347/1632933866532-a63b0ba3-614e-4997-b69a-bc28f5a16f75.png#align=left&display=inline&height=64&margin=%5Bobject%20Object%5D&name=image.png&originHeight=64&originWidth=361&size=13554&status=done&style=none&width=361)
- 说明
- 会生成参数入栈代码
- 如果是c调用约定,会生产平栈代码
- 如果是局部变量取地址,需要使用addr伪指令
- 使用addr的时候,注意ax的使用
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12923347/1632933935701-9a8c54a1-0b7f-45bd-9a9c-a0e3dc22314a.png#align=left&display=inline&height=87&margin=%5Bobject%20Object%5D&name=image.png&originHeight=87&originWidth=398&size=18346&status=done&style=none&width=398)
使用invoke伪指令传参和调用函数示例:
```matlab
assume cs:code,ds:data,ss:stack
data segment ;数据段
g_wVal1 word 1
g_wVal2 word 2
g_sz db "hello word$" ;
g_buf db 255 dup('$')
data ends
stack segment stack
db 128 dup (0)
stack ends
code_seg1 segment
Func1 PROC far C
retf
Func1 ENDP
code_seg1 ends
code segment
;函数名 PROC 距离 调用约定 uses 寄存器环境 参数
Func PROC near C uses bx cx si di pAddr1:word,pAddr2:word
;开始定义局部变量
;局部变量前加@是一种规范而不是语法要求,因为g开头和@开头用于区分局部变量和全局变量一目了然
local @btVal:byte
local @wVal:word
local @dwVal:dword
local @qwVal:qword
local @tbVal:tbyte
mov si,pAddr1
mov di,pAddr2
mov ax,[si]
mov bx,[di]
mov [si],bx
mov [di],ax
mov @btVal,1
mov @wVal,2
;下面三个变量不能这样直接赋值,因为8086是16位的,一次操作宽度不能过大
;mov @dwVal,3
;mov @qwVal,4
;mov @tbVal,5
ret
Func ENDP
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
;=============================code start:
;lea ax,offset g_wVal1
;push ax
;lea ax,offset g_wVal2
;push ax
;call Func
;上面传参改为用 invoke伪指令
invoke Func,offset g_wVal1,offset g_wVal2
;如果想用寄存器的内容,也可以直接放寄存器->invoke Func,offset g_wVal1,bx
;注意,invoke生成的入栈代码,是要用到ax的,如果需要用到ax,则将它放在最后,一般不使用它
call far ptr Func1
;=============================code end:
mov ax,4C00H ;程序退出
int 21H
code ends
end start
需要注意的是,局部变量无法用offset伪指令获取其偏移地址,需要用addr这个伪指令,主要是用于函数调用其它函数时需要传参的某些对应情况。
invoke Func,addr @wVal,addr @dwVal ;Yes
mov ax,addr @wVal ;error...
;addr只适用于传参 且使用addr的时候注意ax的使用
- 函数的声明,类比C语言,如果函数体放到后面,则需要在入口程序之前要声明这个函数
整体示例: ```matlab assume cs:code,ds:data,ss:stack;声明函数 函数名 proto 距离 调用约定 参数
Func proto near C pAddr1:word,pAddr2:word
data segment ;数据段 g_wVal1 word 1 g_wVal2 word 2 g_sz db “hello word$” ; g_buf db 255 dup(‘$’) data ends
stack segment stack db 128 dup (0) stack ends
code_seg1 segment
Func1 PROC far C retf Func1 ENDP
code_seg1 ends
;声明函数 函数名 proto 距离 调用约定 参数 Func proto near C pAddr1:word,pAddr2:word
code segment ;函数名 PROC 距离 调用约定 uses 寄存器环境 参数
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
;=============================code start:
;lea ax,offset g_wVal1
;push ax
;lea ax,offset g_wVal2
;push ax
;call Func
;上面传参改为用 invoke伪指令
invoke Func,offset g_wVal1,offset g_wVal2
;如果想用寄存器的内容,也可以直接放寄存器->invoke Func,offset g_wVal1,bx
;注意,invoke生成的入栈代码,是要用到ax的,如果需要用到ax,则将它放在最后,一般不使用它
call far ptr Func1
;=============================code end: mov ax,4C00H ;程序退出 int 21H
;====================================Func Func PROC near C uses bx cx si di pAddr1:word,pAddr2:word ;开始定义局部变量 ;局部变量前加@是一种规范而不是语法要求,因为g开头和@开头用于区分局部变量和全局变量一目了然 local @btVal:byte local @wVal:word local @dwVal:dword local @qwVal:qword local @tbVal:tbyte
mov si,pAddr1
mov di,pAddr2
mov ax,[si]
mov bx,[di]
mov [si],bx
mov [di],ax
mov @btVal,1
mov @wVal,2
;下面三个变量不能这样直接赋值,因为8086是16位的,一次操作宽度不能过大
;mov @dwVal,3
;mov @qwVal,4
;mov @tbVal,5
ret
Func ENDP ;==================================== code ends end start ```
作业
不使用proc和invoke语法,实现以下函数, 并提交测试代码
strlen, strcpy, strcat, strstr, memcpy, memset, memcmp