目录
从这一章开始,开始以小节为单位视频课本结合看,之前都是看完一章视频后再看课本,当然有不足,那就是不知道视频讲的啥,从这一章开始,以每一小节为单位,并且,小节也是结合看,并没有先后之分,希望这样的学习效率更高些。
第十章讲的是call和ret 指令,他们修改IP或同时修改IP和CS。经常共同实现子程序设计。
10.1 ret和retf
(1) ret:
ip=((ss)*16+(sp))
(sp)=(sp)+2
从而实现ip的修改实现近转移
其实就相当于:
pop ip
(2)retf:
ip=((ss)*16+(sp))
(sp)=(sp)+2
cs=((ss)*16+(sp))
(sp)=(sp)+2
从而实现ip的修改和cs的修改实现远转移
其实就相当于:
pop ip
pop cs
10.2 call指令
cpu执行call指令进行两步操作
(1)将当前的IP或CS 和IP压入栈中
(2)转移
CALL不能实现短转移,另外call转移和jmp指令的原理相同。
10.3依据位移进行转移的CALL指令
CALL标号:
将当前的IP压入栈后再转移到标号处执行指令
(注意当前IP是执行CALL的下一条指令,CALL的下一条指令只是被IP所指过,但是并没有被执行,因为IP立即就改变了)
CPU执行此种格式的CALL指令时,进行如下操作
(1)(sp)=(sp)-2
((ss)*16+(sp))=(IP)
(2)(IP)=(IP)+16位位移
(1)16位位移=标号的偏移地址-call指令的下一条指令的第一个字节的地址
(2)16位位移范围是-32768~32767用补码表示
(3)16位位移由编译程序在编译时算出
CLL 标号
push IP
jmp near ptr 标号
*在dosbox中可以用r cs或者 r IP 修改cs ip 以达到目的去执行相应的指令
*或者用a 指令修改此时所指向的cs:ip的命令
-a jmp 要跳转到的段地址:偏移地址
10.4 转移的目的地址在指令中的CALL指令
前边的CALL指令,其对应的机器码指令中并没有转移的目的地址,而是相对于当前IP的转移位移。
CALL far ptr 标号 实现的是段间偏移
CUP执行时进行的操作
(1)将SP 和SS分别入栈
(sp)=(sp)-2
((ss)*16+(sp))=(CS) *将cs入栈
(sp)=(sp)-2
((ss)*16+(sp))=(IP)
(2)完成跳转
(CS)=标号所在段地址
(IP)=标号所在偏移地址
call far ptr 标号相当于:
push CS
push IP
push far ptr 标号
10.5转移地址在寄存器中的CALL指令
call 16位寄存器
操作
(sp)=(sp)-2
((ss)*16+(sp))=(IP)
上两步操作为入栈操作,将call 寄存器指令的下一条指令的ip入栈
(IP)=(16位reg) *跳转
监测点10.4
用[bp]寻址时默认段地址在ss中
10.6转移地址在内存中的call指令
两种格式:
(1)call word ptr 内存单元地址
push ip
jmp word ptr 内存单元地址
(2)call dword ptr 内存单元地址
push cs *先cs
push ip * 后ip
jmp dword ptr 内存单元地址
会先修改ip(内存单元16位地址) 在修改cs(内存单元高16位)
10.7call和ret指令的配合使用
我们知道call指令是将IP入栈,而ret指令是将IP出栈,那么我们就可以将两者结合起来实现子程序的调用,我所理解的就是C语言中的函数调用,大致框架是这样的,
标号:
指令
ret
具有子程序的云程序框架如下
assume cs:code
code segment
main: ;.............
call sub1 ;调用子程序sub1
mov ax,4C00h
int 21h
sub1:;........ ;子程序sub1开始
call sub 2 ;子程序sub2开始
ret ;子程序返回
sub2:;...
ret ;子程序返回
code ends
end main
10.8 mul指令
mul乘法指令
(1)两个相乘的数:要么都是8位要么都是16位。如果都是8位,一个默认放在AL中,另一个放在8位reg中或者内存字节单元中。如果是16位,一个默认放在AX中,另一个放在16位reg或内存单元字单元中。
(2)结果:8位乘法的结果默认放在AX中,16乘法高位在DX中,低位在AX中。
格式:
mul ret
mul 内存单元
mul byte ptr ds:[0]
mul word ptr [bx+si+8]
10.9模块化程序设计
将复杂的问题转化成简单的问题,使用call和ret指令
10.10参数和结果的传递问题
10.11批量数据的传递
寄存器的数量是有限的,如果数据过多可以将数据放到内存单元中,来进行读取和写入。
10.12寄存器冲突问题
为了避买子程序使用寄存器和主程序使用的寄存器发生冲突,我们通常在子程序的开始将所用到的寄存器push到栈中保护起来,在ret前再将所push的寄存器pop出来(顺序相反)
由于时间问题,索性将课程设计放到最后,所以先不整理课程设计的内容。直接进行下一章的笔记。