本章内容:
1. call 与 ret指令的使用
2.使用call 和ret 对子程序的编写
3. mul指令(乘法)的使用
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
知识点归纳:
现在,个人根据段内转移和段间转移,在程序中调用方法的对应将其归纳为两类,主要功能是实现程序中子程序的编写,相当于高级语言中的函数调用,知识点如下:
一. 段内转移 (仅改IP)
-> 指令:call 标号 \ call word ptr [内存单元地址]
原理:实现段内转移原理相当于以下2个步骤
第1步: push IP
第2步: jmp near ptr 标号 \ jmp word ptr [内存地址]
-> 指令:ret
原理:改IP, 实现段内转移跳出,执行指令完毕后,让IP指向ret指令后的内存单元
第1步: pop IP
二. 段间转移 (改CS:IP)
-> 指令:call far ptr 标号 \ call dword ptr [内存单元地址]
原理:实现段间转移原理相当于以下3个步骤
第1步: push CS
第2步: push IP
第3步: jmp far ptr 标号 \ jmp dword ptr [内存单元地址]
-> 指令:retf
原理:实观段间转移跳出原理相当于以下2个步骤
第1步:pop IP
第2步:pop CS
三:具有子程序的源程序的框架
assume cs:code code segment main: ; 这个程序,两个子程序嵌套了 : ; 一个子程序运行完后,接着会跳到另一个子程序运行 : ; 最后都运行完后,会逐步跳出子程序,回到主程序 call sub1 ; 现开始调用子程序 : : mov ax, 4c00h int 21h sub1 : ; 子程序sub1开始 : : call far ptr sub2 ; 将子程序嵌套在sub1中,调用子程序sub2 : ret ; 子程序返回 sub2: ; 子程序sub2开始 : ; 为了不使寄存器发生冲突,还可以按以下方法编写子程序 : ; 子程序中使用的寄存器入栈 : ; 子程序内容 : ; 子程序中使用的寄存器出栈 retf ; 子程序返回 code ends end main
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
; 程序名称:1009_模块化程序设计_乘法的使用.asm ; 程序功能:计算data段中第一组数据的3次方,结果保存在后面一组dword单元中。 assume cs:code data segment dw 1, 2, 3, 4, 5, 6, 7, 8 ; 即求这一行的每一个数的三次方,结果放入下一行中 dd 0, 0, 0, 0, 0, 0, 0, 0 data ends code segment start: mov ax, data mov ds, ax mov si, 0 ; ds:si 指向第一组word单元 (原数字) mov di, 16 ; ds:di 指向第二组dword单元 (计算结果) mov cx, 8 s: mov bx, [si] call cube ; 调用子程序(功能:计算三次方) mov [di], ax ; 存放计算结果的低位 mov [di].2, dx ; 存放计算结果的高位 add si, 2 ; ds:si指向下一个word单元 add di, 4 ; ds:di指向下一个dword单元 loop s mov ax, 4c00h int 21h cube: ; 子程序cube(功能:计算三次方) mov ax, bx mul bx mul bx ret ; 乘法:mul指令 8位(数值小于255) 16位(数值大于等于255) ; 乘数1 al ax ; 乘数2 8位(内存单元)或(reg) 16位(内存单元)或(reg) ; 结果 ax dx:[ax] code ends end start
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
; 程序名称:1012_模块化程序设计_大小写转换_解决寄存器冲突的问题.asm ; 程序功能:将一个全是字母,以零结尾的字符串,转化为大写。 assume cs:code data segment db 'word', 0 db 'unix', 0 db 'wind', 0 db 'good', 0 data ends code segment start: mov ax, data mov ds, ax mov bx, 0 mov cx, 4 ; 共有四组长度相同的单词 s: mov si, bx call capital ; 调用功能函数 add bx, 5 ; 每组单词的长度为5 loop s mov ax, 4c00h int 21h ; 子程序说明:将一个全是字母,以零结尾的字符串,转化为大写 ; 子程序参数:ds:si指向字符串的首地址 ; 子程序结果:没有返回值 capital: ; 子程序中使用的寄存器入栈 push cx ; 这是为了防止影响主程序 push si change: mov cl, [si] mov ch, 0 jcxz ok ; jcxz 当cx为零时,跳至标号 ; 此处巧妙地利用了cl作为数据结束标记 and byte ptr [si], 11011111b inc si jmp short change ok: pop si ; 寄存器按相应的顺序出栈,数值还原到主程序中。 pop cx ret ; 跳出子程序 code ends end start
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
; 程序名称:实验10_编写子程序_显示字符串.asm ; 程序功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串 ; 子程序描述: ; 名称:show_str ; 功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串 ; 参数:(dh) = 行号(取值范围0~24),(dl) = 列号(取值范围 0~79), ; (cl) = 颜色, ds:si指向字符串的首地址 ; 返回: 无 ; 应用举例:在屏幕的8行3列,用绿色显示data段中的字符串 assume cs:code data segment db 'Welcome to masm!', 0 data ends code segment start: mov dh, 8 ; 行号 mov dl, 3 ; 列号 mov cl, 2 ; 颜色 mov ax, data mov ds, ax ; 将数据段地址放入至ds中 mov si, 0 ; 将si设置为数据段的偏移地址 call show_str mov ax, 4c00h int 21h ; 程序分析: ; 此处要实现显示功能,实际上就是要将ds:[si]的数据搬到B8000H - BFFFFH中 ; 而现在,段地址我将其设为es = B8000, 偏移地址[bx = dh].[di = dl],循环时,di递增 show_str: mov al, dh ; 令[bx = dh] mov ah, 160 ; 每行160个字节 mul ah sub ax, 160 mov bx, ax mov al, dl ; 令[di = dl] mov ah, 0 mov di, ax mov ax, 0B800H ; 设置目的地址 mov es, ax s: mov ch, [si] ; 判断si中的数据是否为零 mov cl, 0 jcxz ok mov al, ds:[si] mov es:[bx+di], al inc di mov ah, 2 ; 颜色 mov es:[bx+di],ah inc di inc si jmp short s ok: ret code ends end start