最近在学习 16位汇编编程,所以顺便将每日所学记录下来,一方面为了巩固学习的知识,另一方面也为同样在学习汇编开发的童鞋们提供一份参考。
运行环境:
- 操作系统: Windows 10家庭版
- 编译器:Windows XP Debug
子程序指令
- 子程序是完成特定功能的一段程序
- 当主程序需要执行这个功能是,采用call指令调用指令转移到子程序的起始处执行
- 当运行完子程序功能,采用RET返回指令回到主程序继续执行
CALL指令
demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| MyStack segment stack db 256 dup (?) MyStack ends
MyData segment g_szHello db 'Hello World!',0dh,0ah,'$' MyData ends
MyCode segment MyPuts: mov dx,offset g_szHello mov ah,9 int 21h ;ret pop ax jmp ax
start: mov ax,MyData mov ds,ax ;call MyPuts mov ax,offset Next ;模拟call指令 push ax jmp MyPuts Next: mov ax,4c00h int 21h MyCode ends end start
|
如果我们需要call的子程序中存在参数,对上述代码进行修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| MyStack segment stack db 256 dup (?) MyStack ends
MyData segment g_szHello db 'Hello World!',0dh,0ah,'$' MyData ends
MyCode segment MyPuts: mov dx,cx ;寄存器传参 mov ah,9 int 21h ret
start: mov ax,MyData mov ds,ax mov cx,offset g_szHello ;使用寄存器存放变量的偏移地址 call MyPuts Next: mov ax,4c00h int 21h MyCode ends end start
|
以上代码会占用寄存器资源,我们可以使用栈传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| MyStack segment stack db 256 dup (?) MyStack ends
MyData segment g_szHello db 'Hello World!',0dh,0ah,'$' MyData ends
MyCode segment MyPuts: mov bp,sp mov ax,[bp + 2] ;寄存器传参 mov dx,ax mov ah,9 int 21h ret
start: mov ax,MyData mov ds,ax xor cx,cx ;int cx = 0; LOOP_BEGIN: cmp cx,10 jg LOOP_END ;while(cx < 10){
mov ax,offset g_szHello ;使用寄存器存放变量的偏移地址 push ax call MyPuts add sp,2 ;调用方负责平衡堆栈
inc cx ;cx++ jmp LOOP_BEGIN ;}
LOOP_END: mov ax,4c00h int 21h MyCode ends end start
|
CALL指令其实是做了两个动作,将CALL下一条指令的IP入栈,在JMP到指定的函数的地址处。
1 2 3 4 5
| 段内调用——入栈偏移地址IP SP←SP-2,SS:[SP]←IP 段间调用——入栈偏移地址IP和段地址CS SP←SP-2,SS:[SP]←IP SP←SP-2,SS:[SP]←CS
|
RET
1.根据段内和段间、有无参数,分成4种类型
- RET(或者RETN) ;无参数段内返回
- RET(或者RETN) i16 ;有参数段内返回
- RETF ;无参数段间返回
- RETF i16 ;有参数段间返回
2.需要弹出CALL指令压入堆栈的返回地址
段内返回——出栈偏移地址IP
IP←SS:[SP], SP←SP+2
段间返回——出栈偏移地址IP和段地址CS
IP←SS:[SP],SP←SP+2
CS←SS:[SP],SP←SP+2
RET指令的作用其实就是POP IP(IP只有系统可以修改)
RETF指令的作用其实就是POP IP 和POP CS
段间转移必须写call far ptr labal,并且必须与retf相配合