• LOOP
    • 格式:loop标号
    • 只能用于短转移
    • image.png

函数

  • 函数结构
  • 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指向的数据保存在局部变量

  1. mov bx,ss:[bp+6]
  2. mov ax,ds:[bx]
  3. mov bx,ss:[bp+4]
  4. mov ds:[bx],ax
  5. mov bx,ss:[bp+6]
  6. mov ax,ss:[bp-2]
  7. mov [bx],ax
  8. ;恢复现场
  9. pop di
  10. pop si
  11. pop bx
  12. ;降低堆栈
  13. mov sp, bp
  14. pop bp
  15. ret 4

start:
mov ax,stack mov ss,ax mov sp,128

  1. mov ax,data
  2. mov ds,ax

;=============================code start:

  1. ;push 前,sp=80h,bp=00h
  2. lea ax,offset g_a
  3. push ax ;00h
  4. lea ax,offset g_b
  5. push ax ;02h
  6. ;call 前,sp=7ch,bp=00h
  7. call SWAP
  8. mov ax,[g_a]
  9. mov bx,[g_b]

;=============================code end: mov ax,4C00H ;程序退出 int 21H code ends end start

  1. 两种平栈方式:ret imm(相当于pop ip sub sp,imm call 后面更 add sp,imm ;imm是根据入栈参数所占的word数来决定的
  2. - 注意,下图功能只有push那行,没有写完整
  3. ![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)
  4. <a name="WL0IT"></a>
  5. ## masm函数语法
  6. - 函数语法
  7. - ![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)
  8. - 示例
  9. - ![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)
  10. - 简单示例(无函数功能)
  11. ```matlab
  12. assume cs:code,ds:data,ss:stack
  13. data segment ;数据段
  14. g_a word 1
  15. g_b word 2
  16. g_sz db "hello word$" ;
  17. g_buf db 255 dup('$')
  18. data ends
  19. stack segment stack
  20. db 128 dup (0)
  21. stack ends
  22. code segment
  23. Func PROC
  24. ret
  25. Func ENDP
  26. start:
  27. mov ax,stack
  28. mov ss,ax
  29. mov sp,128
  30. mov ax,data
  31. mov ds,ax
  32. ;=============================code start:
  33. call Func
  34. ;=============================code end:
  35. mov ax,4C00H ;程序退出
  36. int 21H
  37. code ends
  38. end start
  • 距离

image.png
段内其实加不加near都是一样的,如同上面示例的Func
例:

  1. assume cs:code,ds:data,ss:stack
  2. data segment ;数据段
  3. g_a word 1
  4. g_b word 2
  5. g_sz db "hello word$" ;
  6. g_buf db 255 dup('$')
  7. data ends
  8. stack segment stack
  9. db 128 dup (0)
  10. stack ends
  11. code_seg1 segment
  12. Func1 PROC far
  13. retf
  14. Func1 ENDP
  15. code_seg1 ends
  16. code segment
  17. Func PROC
  18. ret
  19. Func ENDP
  20. start:
  21. mov ax,stack
  22. mov ss,ax
  23. mov sp,128
  24. mov ax,data
  25. mov ds,ax
  26. ;=============================code start:
  27. call Func
  28. call far ptr Func1
  29. ;=============================code end:
  30. mov ax,4C00H ;程序退出
  31. int 21H
  32. code ends
  33. end start
  • 调用约定
  • image.png
  • c调用约定外平栈,stdcall内平栈

image.png
示例:定义局部变量和使用调用约定以及寄存器环境

  1. assume cs:code,ds:data,ss:stack
  2. data segment ;数据段
  3. g_wVal1 word 1
  4. g_wVal2 word 2
  5. g_sz db "hello word$" ;
  6. g_buf db 255 dup('$')
  7. data ends
  8. stack segment stack
  9. db 128 dup (0)
  10. stack ends
  11. code_seg1 segment
  12. Func1 PROC far C
  13. retf
  14. Func1 ENDP
  15. code_seg1 ends
  16. code segment
  17. ;函数名 PROC 距离 调用约定 uses 寄存器环境 参数
  18. Func PROC near stdcall uses bx cx si di pAddr1:word,pAddr2:word
  19. ;开始定义局部变量
  20. ;局部变量前加@是一种规范而不是语法要求,因为g开头和@开头用于区分局部变量和全局变量一目了然
  21. local @btVal:byte
  22. local @wVal:word
  23. local @dwVal:dword
  24. local @qwVal:qword
  25. local @tbVal:tbyte
  26. ret
  27. Func ENDP
  28. start:
  29. mov ax,stack
  30. mov ss,ax
  31. mov sp,128
  32. mov ax,data
  33. mov ds,ax
  34. ;=============================code start:
  35. lea ax,offset g_wVal1
  36. push ax
  37. lea ax,offset g_wVal2
  38. push ax
  39. call Func
  40. call far ptr Func1
  41. ;=============================code end:
  42. mov ax,4C00H ;程序退出
  43. int 21H
  44. code ends
  45. 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

  1. mov @btVal,1
  2. mov @wVal,2
  3. ;下面三个变量不能这样直接赋值,因为808616位的,一次操作宽度不能过大
  4. ;mov @dwVal,3
  5. ;mov @qwVal,4
  6. ;mov @tbVal,5
  7. ret

Func ENDP start:
mov ax,stack mov ss,ax mov sp,128

  1. mov ax,data
  2. mov ds,ax

;=============================code start:

  1. lea ax,offset g_wVal1
  2. push ax
  3. lea ax,offset g_wVal2
  4. push ax
  5. call Func
  6. call far ptr Func1

;=============================code end: mov ax,4C00H ;程序退出 int 21H code ends end start

  1. - invoke伪指令
  2. - ![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)
  3. - 说明
  4. - 会生成参数入栈代码
  5. - 如果是c调用约定,会生产平栈代码
  6. - 如果是局部变量取地址,需要使用addr伪指令
  7. - 使用addr的时候,注意ax的使用
  8. - ![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)
  9. 使用invoke伪指令传参和调用函数示例:
  10. ```matlab
  11. assume cs:code,ds:data,ss:stack
  12. data segment ;数据段
  13. g_wVal1 word 1
  14. g_wVal2 word 2
  15. g_sz db "hello word$" ;
  16. g_buf db 255 dup('$')
  17. data ends
  18. stack segment stack
  19. db 128 dup (0)
  20. stack ends
  21. code_seg1 segment
  22. Func1 PROC far C
  23. retf
  24. Func1 ENDP
  25. code_seg1 ends
  26. code segment
  27. ;函数名 PROC 距离 调用约定 uses 寄存器环境 参数
  28. Func PROC near C uses bx cx si di pAddr1:word,pAddr2:word
  29. ;开始定义局部变量
  30. ;局部变量前加@是一种规范而不是语法要求,因为g开头和@开头用于区分局部变量和全局变量一目了然
  31. local @btVal:byte
  32. local @wVal:word
  33. local @dwVal:dword
  34. local @qwVal:qword
  35. local @tbVal:tbyte
  36. mov si,pAddr1
  37. mov di,pAddr2
  38. mov ax,[si]
  39. mov bx,[di]
  40. mov [si],bx
  41. mov [di],ax
  42. mov @btVal,1
  43. mov @wVal,2
  44. ;下面三个变量不能这样直接赋值,因为8086是16位的,一次操作宽度不能过大
  45. ;mov @dwVal,3
  46. ;mov @qwVal,4
  47. ;mov @tbVal,5
  48. ret
  49. Func ENDP
  50. start:
  51. mov ax,stack
  52. mov ss,ax
  53. mov sp,128
  54. mov ax,data
  55. mov ds,ax
  56. ;=============================code start:
  57. ;lea ax,offset g_wVal1
  58. ;push ax
  59. ;lea ax,offset g_wVal2
  60. ;push ax
  61. ;call Func
  62. ;上面传参改为用 invoke伪指令
  63. invoke Func,offset g_wVal1,offset g_wVal2
  64. ;如果想用寄存器的内容,也可以直接放寄存器->invoke Func,offset g_wVal1,bx
  65. ;注意,invoke生成的入栈代码,是要用到ax的,如果需要用到ax,则将它放在最后,一般不使用它
  66. call far ptr Func1
  67. ;=============================code end:
  68. mov ax,4C00H ;程序退出
  69. int 21H
  70. code ends
  71. end start

需要注意的是,局部变量无法用offset伪指令获取其偏移地址,需要用addr这个伪指令,主要是用于函数调用其它函数时需要传参的某些对应情况。

  1. invoke Func,addr @wVal,addr @dwVal ;Yes
  2. mov ax,addr @wVal ;error...
  3. ;addr只适用于传参 且使用addr的时候注意ax的使用
  • 函数的声明,类比C语言,如果函数体放到后面,则需要在入口程序之前要声明这个函数
    1. ;声明函数 函数名 proto 距离 调用约定 参数
    2. Func proto near C pAddr1:word,pAddr2:word
    整体示例: ```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

;声明函数 函数名 proto 距离 调用约定 参数 Func proto near C pAddr1:word,pAddr2:word

code segment ;函数名 PROC 距离 调用约定 uses 寄存器环境 参数

start:
mov ax,stack mov ss,ax mov sp,128

  1. mov ax,data
  2. mov ds,ax

;=============================code start:

  1. ;lea ax,offset g_wVal1
  2. ;push ax
  3. ;lea ax,offset g_wVal2
  4. ;push ax
  5. ;call Func
  6. ;上面传参改为用 invoke伪指令
  7. invoke Func,offset g_wVal1,offset g_wVal2
  8. ;如果想用寄存器的内容,也可以直接放寄存器->invoke Func,offset g_wVal1,bx
  9. ;注意,invoke生成的入栈代码,是要用到ax的,如果需要用到ax,则将它放在最后,一般不使用它
  10. 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

  1. mov si,pAddr1
  2. mov di,pAddr2
  3. mov ax,[si]
  4. mov bx,[di]
  5. mov [si],bx
  6. mov [di],ax
  7. mov @btVal,1
  8. mov @wVal,2
  9. ;下面三个变量不能这样直接赋值,因为808616位的,一次操作宽度不能过大
  10. ;mov @dwVal,3
  11. ;mov @qwVal,4
  12. ;mov @tbVal,5
  13. ret

Func ENDP ;==================================== code ends end start ```

作业

不使用proc和invoke语法,实现以下函数, 并提交测试代码
strlen, strcpy, strcat, strstr, memcpy, memset, memcmp