ASM16 21 函数补充和中断指令
函数补充
1 | MyStack segment stack |
代码解析
子程序段
1 | MyCode2 segment |
MyCode2 segment
声明一个代码段。call far ptr MY_SUB
进行远调用,跳转到MY_SUB
子程序。(虽然MY_SUB
在同一个段,但因为其他段也要调用MY_SUB
,故MY_SUB
中对参数的调用按照远调用的格式来写,所以此处也采用远调用,保证格式的一致)MY_SUB:
标识子程序的开始。push bp
保存基指针寄存器bp
的当前值。mov bp,sp
将栈指针寄存器sp
的值复制到基指针寄存器bp
中,保存当前栈帧。mov ax,[bp+6]
将偏移bp
加上 6 的位置处的值移动到ax
寄存器中。sub ax,[bp+8]
将ax
寄存器的值减去偏移bp
加上 8 的位置处的值。pop bp
恢复栈底,弹出先前保存的bp
值。retf 4
远返回并清除4个字节的参数。MyCode2 ends
表示代码段结束。
主代码段
1 | MyCode segment |
MyCode segment
声明代码段。MAIN:
标识程序的入口点。mov ax,MyData
将数据段地址加载到ax
寄存器。mov ds,ax
设置数据段寄存器ds
。mov es,ax
设置附加段寄存器es
。
接下来的代码进行段间子程序调用:
mov ax,1
将数值1加载到ax
寄存器。push ax
将ax
寄存器的值压入栈。mov ax,2
将数值2加载到ax
寄存器。push ax
将ax
寄存器的值压入栈。call far ptr MY_SUB
调用远子程序MY_SUB
。(因为MY_SUB
在另一个段,故要采用远调用)
调用显示消息的子程序:
mov ax,09h
将参数2加载到ax
。push ax
将参数2压入栈。mov ax,offset MY_MSG1
将消息1的偏移地址加载到ax
。push ax
将参数1压入栈。call SHOW_HELLO
调用SHOW_HELLO
子程序。
重复调用 SHOW_HELLO
显示第二条消息:
mov ax,09h
将参数2加载到ax
。push ax
将参数2压入栈。mov ax,offset MY_MSG2
将消息2的偏移地址加载到ax
。push ax
将参数1压入栈。call SHOW_HELLO
调用SHOW_HELLO
子程序。
调用加法子程序:
mov ax,2
将数值2加载到ax
。push ax
将数值2压入栈。mov ax,1
将数值1加载到ax
。push ax
将数值1压入栈。call MY_ADD
调用MY_ADD
子程序。
结束程序:
mov ax,4c00h
将程序终止返回码加载到ax
。int 21h
调用DOS中断终止程序。
子程序 SHOW_HELLO
1 | SHOW_HELLO: |
把mov bp,sp
放到保护环境前,则访问参数时的偏移固定从+4开始,就不用考虑压栈了多少个寄存器
子程序 MY_ADD
1 | MY_ADD: |
MY_ADD:
标识子程序的开始。push bp
保存基指针寄存器bp
的当前值。mov bp,sp
将栈指针寄存器sp
的值复制到基指针寄存器bp
中,保存当前栈帧。sub sp,4
分配4个字节的局部变量空间。push bx
保存bx
寄存器的值。
处理加法运算:
mov ax,[bp+4]
将第一个参数(第一个加数)加载到
ax
寄存器。
mov [bp-2],ax
将ax
的值存储到第一个局部变量中。add ax,[bp+6]
将第二个参数(第二个加数)加到ax
寄存器中。mov [bp-4],ax
将结果存储到第二个局部变量中。
恢复寄存器和返回值:
pop bx
恢复bx
寄存器的值。mov sp,bp
释放局部变量空间。pop bp
恢复栈底。retn 4
返回并清除4个字节的参数。
调试
远调用会把跳转的地址显示出来
将要跳转回来的地址都存进堆栈
返回后CS和IP的值都被改变,正是刚才被压入堆栈的地址
运行完减法函数后ax会保存返回值(2-1=1)
运行完加法函数(1+2=3)
理论补充
子程序指令
- 子程序是完成特定功能的一段程序
- 当主程序(调用程序)需要执行这个功能时,采用call调用指令转移到该子程序的起始处执行
- 当运行完子程序功能后,采用RET返回指令回到主程序继续执行
子程序调用指令
CALL指令分为4种类型(类似JMP)
1
2
3
4call label ;段内调用、直接寻址
call r16/m16 ;段内调用、间接寻址
call far ptr label ;段间调用、直接寻址
call far ptr mem ;段间调用、间接寻址CALL指令需要保存返回地址:
子程序返回指令
根据段内和段间、有无参数,分为4种类型
1
2
3
4RET ;无参数段内返回
RET i16 ;有参数段内返回
RETF ;无参数段间返回
RETF i16 ;有参数段间返回需要弹出CALL指令压入堆栈的返回地址
中断指令
- 中断(Interrupt)是又一种改变程序执行顺序的方法
- 中断具有多种中断类型
- 中断的指令有3条:
INT i8
IRET
INTO
- 本节主要掌握类似子程序调用指令的中断调用指令
INT i8
,进而学习使用DOS功能调用
1 | INT i8 ;中断调用指令:产生i8好=号中断 |
字符串输出的功能调用
DOS功能调用
INT 21H
可以输出回车(0DH)和换行(0AH)字符产生回车和换行的作用
DOS功能调用
INT 21H
执行该功能调用时,用户按键,最后用回车确认
本调用可执行全部标准键盘编辑命令;用户按回车键结束输入,如按Ctrl+Break或Ctrl+C则中止
字符输出的功能调用
- 显示器功能调用
INT 10H
按键判断的功能调用
- DOS功能调用
INT 21H
键盘功能调用
INT 16H
这两个功能调用都不循环等待按键,即使有键按下,键盘缓冲区仍然保留键值并且没有被清空,必要时必须用字符输入功能取走键值清空缓冲区
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hexo!