赞
踩
检测点10.1
补全程序,使从内存1000:000处开始执行指令。
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,10h
mov ax,1000 ;由程序下面的转移指令retf可知,首先入栈的是内存段地址
push ax
mov ax,0 ;其次入栈的是偏移地址
push ax
retf
此问题较为简单,
检测点10.2
程序执行后,ax中的数值是多少?
ax=6
;分析
1)当cs:ip指向1000:3时,‘call s’指令的机器码进入指令缓冲器,ip指向下一条指令开始处(6);
2)CPU执行’call s’。将当前ip压入栈(6),并修改当前ip为标号s的偏移地址;程序跳转到1000:7处执行(即标号s处)。
3)执行’pop ax’。
所以,ax的值是6.
检测点10.3
程序执行后,ax中的数值是多少?
ax=1010h
分析:
1)CPU执行mov ax,0
,ax=0
2)CPU将call far ptr s
指令机器码加载入指令缓冲器,ip指向下一指令(8)
之后CPU执行该指令,做push cs,push ip ,jmp s
操作。执行后,栈中数据为0800,0010;cs:ip指向标号s处(pop ax
)
3)CPU执行pop ax
,ax=8
4)CPU执行add ax,ax
,ax=10h
5)CPU执行pop bx
,bx=1000h
6)CPU执行add ax,bx
ax=1010h
检测点10.4
程序执行后,ax中的数值是多少?
ax=0bh
分析:
1)CPU执行mov ax,6
,ax=6
2)CPU执行call ax
(执行过程与上述一致,不再赘述),执行后:栈中ss:sp=0500,cs:ip=1000:6
3)CPU执行mov bp,sp
,bp=sp
4)CPU执行add ax,[bp]
,内存[bp]的默认段寄存器为ss。故ax=6+5=0bh
检测点10.5
(1)程序执行后,ax中的值为多少(不要用debug来验证结论)
assume cs:code stack segment dw 8 dup (0) stack ends code segment start: mov ax,stack mov ss,ax mov sp,10h ;使ss:sp指向栈顶(栈为空) mov ds,ax ;使ds指向栈段 mov ax,0 call word ptr ds:[0eh] ;1)先让ip指向下一条指令,再将当前ip的的偏移地址(下一条指令的ip地址) 入栈。再修改ip的值为(ds:[0eh]),即将ip修该为下一条指令处 inc ax inc ax inc ax mov ax,4c00h int 21h code ends end start
ax=3
(2)程序执行后,ax和bx中的数值是多少?
assume cs:code data segment dw 8 dup (0) data ends code segment start: mov ax,data mov ss,ax mov sp,16 ;使ss:ip指向栈顶(此时栈空) mov word ptr ss:[0],offset s ;将标号s处的偏移地址送入ss:[0] mov ss:[2],cs ;将cs的值送入ss:[2] call dword ptr ss:[0] ;将`nop`指令的偏移地址入栈,再将cs的值入栈; 再将ip的值修改为(ss:[0]),将cs的值修改为(ss:[2]) 即挑战到标号s处 nop s: mov ax,offset s ;将标号s的偏移地址送入ax中 sub ax,ss:[0ch] ;将(ss:[0ch])-ax的值送入ax中 注:ss:[0ch]是'nop'的偏移,ax中是s的偏移,故执行后ax=1 mov bx,cs ;将cx送入bx中 sub bx,ss:[0eh] ;注:ss:[0e]处存放的是cs的值,二者相减,为0 mov ax,4c00h int 21h code ends end start
ax=1,bx=0
**
1.显示字符串
子程序描述:
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh )=行号(范围:0~24),(dl)=列号(范围:0-79),
(cl)=颜色,ds:si指向字符串的首地址;
返回:无
思路:
此子程序主要考虑显存地址与字符串的对应关系,前面做过类似实验。
以下,为我的代码;
"""说明:主程序主要提供参数和要显示的内容,具体操作由子程序完成。""" assume cs:code data segment db 'Welcome to LOL!!Nice to meet you!!',0 #db 'Hello world!',0 data ends code segment start: mov ax,data mov ds,ax mov si,0 ;使ds指向要显示的字符串首地址 mov dh,8 #mov dh,13 mov dl,3 #mov dh,7 mov cx,2 ;设置要显示的行,列及颜色。 #mov dh,6 call show_str mov ax,4c00h int 21h #子程序 """ 名称:show_str 功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。 所需参数:(dh )=行号(范围:0~24),(dl)=列号(范围:0-79), (cl)=颜色,ds:si指向字符串的首地址; 返回:无 """ show_str: push ax push bx push cx push dx push si push di push ds push es ;将子程序所有用到的寄存器入栈保存,方便调用。 #将参数转化为显存上的地址。其对应关系为:行起始=0a0h*行号;列起始=02h*列号; mov al,0a0h mul dh mov bx,ax ;对应显存的行 mov al,2 mul dl mov di,ax ;对应显存的列 mov ax,0b800h mov es,ax ;用es:[bx].[di]来寻址显存 mov ah,cl ;先将颜色用ah保存,因为一开始就要用cx进行判断 #完成字符串到显存的传送 trans: mov cx,ds:[si] jcxz back ;判断要传送的字符是否为‘0’,为0则直接返回。 mov al,ds:[si] mov es:[bx].[di],al ;传送字符到奇数列,对应显存上的ASCII码 mov es:[bx].1[di],ah ;传送颜色到偶数列,对应显存上的颜色 inc si add di,2 ;控制字符串及显存地址指向下一个单元 jmp short trans back: pop es pop ds pop di pop si pop dx pop cx pop bx pop ax ret code ends end start
以下为运行结果,多次运行,改变条件,测试它的通用性;
2.解决除法溢出问题。
问题描述:在用div做除法的时候,结果的商过大,超出了寄存器所能存储的范围。发生错误(除法溢出)。写一个子程序使除法运算不会溢出。
子程序描述:
名称:divdw
功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
参数:(ax)=dword型数据的低16位
(dx)=dword型数据的高16位
(cx)=除数
返回:(dx)=结果的高16位,(ax)=结果的低16位,(cx)=余数
思路:
此子程序主要在于此公式的理解,此公式将可能有溢出的除法运算拆分为肯定不会溢出的运算。首先将被除数的高16位作为16位除法运算(H/N)的低16位,高16位补0。这样在(H/N)运算中,被除数和除数相差绝对不会超过65535;同理(H/N)运算后的余数也不会大。再将(H/N)运算后的商左移4位后作为原除法运算的高16位,将(H/N)运算后的[(余数左移4位+原被除数的低16位)/N]的结果的商作为原除法运算的低16位,余数作为原除法运算的余数。
*感觉表达不是太清楚,*?
以下,为我的代码:
"""由主程序提供要可能发生溢出的除法运算的数值,子程序完成不会溢出的返回值""" assume cs:code #此段用来保存子程序的返回值。 data segment dw 8 dup (0) data ends code segment start: mov ax,data mov ds,ax ;使ds指向data段 mov ax,4240h #mov ax,0e8e0h mov dx,000fh #mov dx,005eh mov cx,0ah ;向子程序提供的参数,除数,被除数 call divdw mov dx,ds:[0] mov ax,ds:[2] mov cx,ds:[4] ;取返回值。 mov ax,4c00h int 21h #子程序 """ 子程序描述: 名称:divdw 功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。 所需参数:(ax)=dword型数据的低16位 (dx)=dword型数据的高16位 (cx)=除数 返回:(dx)=结果的高16位,(ax)=结果的低16位,(cx)=余数 """ divdw: push ax push cx push dx push ds ;保护寄存器的值 push ax ;保存L,ax传进来的是公式中的L值 mov ax,dx ;将公式中的H传入ax中,作为(H/N)中被除数的低16位 mov dx,0 ;将(H/N)中被除数的高16位置0 div cx ;进行(H/N) mov ds:[0],ax ;将(H/N)的商(int(H/N))保存到返回值段处,(作为无溢出除法运算商的高16位) pop ax ;注:rem(H/N)*65536相当于左移4位,再加上L后,刚好L可作为 下步除法运算的被除数的低16位;在ax中存放 上步(H/N)的余数即为rem(H/N),作为新除法的被除数的高16位,在dx中存放 div cx ;进行(rem(H/N)*65536+L)/N除法运算 mov ds:[2],ax ;保存结果的商到返回值段(作为无溢出除法运算的商的低16位) mov ds:[4],dx ;保存结果的余数到返回值段(作为无溢出除法运算的余数) pop ds pop dx pop cx pop ax ret
以下为运行结果(在debug中查看)
(1)选取除数10(0ah),被除数1000000(0f4240h),结果100000(186a0h),余数0(0h)
的测试结果
(2)选取除数10(0ah),被除数6220000(5ee8e0h),结果622000(97db0h),余数0(0h)
的测试结果
(3)选取除数10(0ah),被除数65533(0fffdh),结果6553(1999h),余数3(3h)
的测试结果
(4)选取除数14(0eh),被除数778(30ah),结果55(37h),余数8(8h)
的测试结果
经过多轮测试,此子程序可完成16位,32位的不会溢出的除法运算。
3.数值显示
assume cs:code #此段用来存放数字的字符串 data segment db 100 dup (0) data ends code segment start: mov ax,data mov ds,ax mov si,0 ;使ds:si指向字符串首地址(作为show_str子程序的参数) mov ax,12666 ;将要显示的数字 #mov ax,1 call dtoc ;调用dtoc子程序,进行数据转为ascii码 mov dh,8 mov dl,3 mov cl,2 ;设置show_str的参数 call show_str ;调用show_str进行显示 mov ax,4c00h int 21h """ 子程序描述: 名称:dtoc 功能:将word型数据转变为表示十进制数的字符串字符串以0为结尾符。 参数:(ax)=word型数据 ds:si指向字符串的首地址 返回:无 """ #子程序 dtoc: push ax push bx push cx push dx push si push ds mov bx,0 ;将bx作为计数器。记录入栈次数。 change: mov dx,0 mov cx,10 div cx ;进行16位除法 add dx,30h ;dx中为除法运算的余数 push dx inc bx ;将表示10进制的字符串入栈保存,并使入栈次数加一 mov cx,ax ;将商作为判断是否结束的依据 jcxz rev jmp short change rev: mov cx,bx ;将入栈次数作为循环次数 save: pop ax mov ds:[si],al inc si ;进行转存 loop save inc si mov byte ptr ds:[si],0 ;将表示10进制字符串的后边补0作为其结束符。 pop ds pop si pop dx pop cx pop bx pop ax ret #子程序描述 """ 名称:show_str 功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。 所需参数:(dh )=行号(范围:0~24),(dl)=列号(范围:0-79), (cl)=颜色,ds:si指向字符串的首地址; 返回:无 """ show_str: push ax push bx push cx push dx push si push di push ds push es ;将子程序所有用到的寄存器入栈保存,方便调用。 #将参数转化为显存上的地址。其对应关系为:行起始=0a0h*行号;列起始=02h*列号; mov al,0a0h mul dh mov bx,ax ;对应显存的行 mov al,2 mul dl mov di,ax ;对应显存的列 mov ax,0b800h mov es,ax ;用es:[bx].[di]来寻址显存 mov ah,cl ;先将颜色用ah保存,因为一开始就要用cx进行判断 #完成字符串到显存的传送 trans: mov cx,ds:[si] jcxz back ;判断要传送的字符是否为‘0’,为0则直接返回。 mov al,ds:[si] mov es:[bx].[di],al ;传送字符到奇数列,对应显存上的ASCII码 mov es:[bx].1[di],ah ;传送颜色到偶数列,对应显存上的颜色 inc si add di,2 ;控制字符串及显存地址指向下一个单元 jmp short trans back: pop es pop ds pop di pop si pop dx pop cx pop bx pop ax ret code ends end start
运行结果:
注:(dtoc的参数ax最大值为65535,超出会报错。)
本节内容挺难顶的,不过还好,我给它全部搞完了。
(本章知识点记录)
call和ret都是转移指令,都可以修改ip,或同时修改cs和ip。
10.1 ret和retf
ret指令用栈中的数据,修改ip的内容,实现近转移;
retf指令用栈中的数据,修改cs和ip的内容,实现远转移。
CPU执行ret指令时,进行下面两步操作:
(1)(IP)=((ss)*16+(sp))
(2)(sp)=(sp)+2
相当于进行 pop ip
CPU执行retf指令时,进行下面4步操作:
(1)(IP)=((ss)*16+(sp))
(2)(sp)=(sp)+2
相当于进行pop ip,popcs
10.2 call指令
CPU执行call指令时,进行两步操作:
(1)将当前的ip或cs和ip压入栈;
(2)转移。
不能进行短转移。
10.3依据位移进行转移的call指令
格式:call 标号(将当前的ip压栈后,转到标号处执行。)
进行的操作:
(1)(sp)=((sp)-2)
((ss)*16+(sp))=(ip)
(2)(ip)=(ip)+16位位移
相当于进行:push ip,jmp near ptr 标号
10.4转移的目的地址在指令中的call指令
格式:call far ptr 标号 实现段间转移。
进行的操作:
(1)((sp))=((sp)-2)
((ss)*16+(sp))=(cs)
((sp))=((sp)-2)
((ss)*16+(sp))=(ip)
(2)(ip)=(ip)+16位位移
相当于进行:push cs,push ip ,jmp far ptr 标号
10.5转移地址在寄存器中的call指令
格式:call 16位 reg
进行的操作:
(1)(sp)=(sp)-2
((ss)*16+(sp))=(ip)
(2)(ip)=(16位reg)
相当于进行:push IP,jmp 16位reg
10.6转移地址在内存中的call 指令
格式:call word ptr 内存单元地址 或者
call dword ptr 内存单元地址
(1)相当于进行:push ip,jmp near ptr 内存字单元
(2)相当于进行:push cs,push ip ,jmp dword ptr 内存双字单元
10.7 call和ret的配合使用
可以写一段具有一定功能的程序段,称其为子程序,在需要的时候,用call指令,转去执行,在子程序中使用ret指令,使程序转到call指令后面的代码处继续执行。(相当于函数的功能。)
call 和ret 子程序框架:
标号:
指令
ret
具有子程序的源程序框架:
assume cs:code code segment main: : : call sub1 ;调用子程序sub1 : : mov ax,4c00h int 21h sub1: : : call sub2 ;调用子程序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中。
10.9 模块化程序设计
10.10 参数和结果传递问题
10.11 批量数据的传递
子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者。实际上就是讨论如何存储子程序需要的参数和产生的返回值。
(1)用寄存器来存储参数和结果是最常用的方法。
调用者将参数送入参数寄存器,从结果寄存器中取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。
(2)将要传递的批量数据放到内存,然后将它们所在的内存空间首地址放在寄存器中,传递给需要的子程序;返回结果亦是如此。
(3)用栈来传递参数
这种技术和高级语言编译器的工作原理密切相关。
就是由调用者将需要传递给子程序的参数压入栈中,子程序从栈中取得参数。
**注:**指令:ret n :相当于进行pop ip , add sp,n
C语言中,局部变量也在栈中存储。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。