实验3 转移指令跳转原理及其简单应用编程
实验任务1
task1.asm
assume cs:code, ds:data data segment x db 1, 9, 3 ;db中b表byte,一个字节 len1 equ $ - x ;符号常量,$表一个数据项的偏移地址,此处为3 y dw 1, 9, 3 ;dw中w表word,两个字节 len2 equ $ - y ;符号常量,$表一个数据项的偏移地址,此处为9(3 + 6) data ends code segment start: mov ax, data mov ds, ax ;数据处理 mov si, offset x ;取符号x对应的偏移地址0 -> si mov cx, len1 ;从符号x开始的连续字节数据项个数 mov ah, 2 ;ah==2时显示输出dl中的字符字段 s1:mov dl, [si] ;si表示x对应的偏移地址,将x的第一个数据移到dl中 or dl, 30h ;转化成ASCII码 int 21h mov dl, ' ' ;输出空格 int 21h inc si ;移到下一个数据 loop s1 ;继续执行s1 mov ah, 2 ;显示输出dl中的字符字段 mov dl, 0ah ;0ah的ASCII值表示换行符:输出换行符 int 21h mov si, offset y ;将y的偏移量移到si中 mov cx, len2/2 ;因为y的数据间隔是两个字节,数量就是字节总数/2 mov ah, 2 ;显示输出dl s2:mov dx, [si] ;之后操作同s1 or dl, 30h int 21h mov dl, ' ' int 21h add si, 2 loop s2 mov ah, 4ch int 21h code ends end start
实验结果
回答问题
①0019-000d=000c=12 所以偏移量是12。cpu角度分析:读取loop s1指令进入指令缓冲器。根据读取指令的长度,修改IP的值,(IP)=(IP)+所读取指令的长度,从而指向下一条指令。执行loop 指令,跳转到s1处,s1处的偏移地址减去当前IP所指向的内存单元的偏移地址就是跳转后的偏移地址。
②0037-0029=000e=14 所以偏移量是14。cpu角度分析:读取loop s2指令进入指令缓冲器。根据读取指令的长度,修改IP的值,(IP)=(IP)+所读取指令的长度,从而指向下一条指令。执行loop 指令,跳转到s2处,s2处的偏移地址减去当前IP所指向的内存单元的偏移地址就是跳转后的偏移地址。
③在debug中进行调试观察的反汇编截图
实验任务2
task2.asm
assume cs:code, ds:data data segment dw 200h, 0h, 230h, 0h data ends stack segment db 16 dup(0) stack ends code segment start: mov ax, data mov ds, ax mov word ptr ds:[0], offset s1 ;word ptr表示这是一个字,将s1的偏移地址移入ds:[0](ds:[1])中 mov word ptr ds:[2], offset s2 mov ds:[4], cs ;cs表段地址,移到ds:[4]中(ds:[5]) mov ax, stack mov ss, ax ;将栈的段地址移入ss中 mov sp, 16 ;预留16字节的栈空间 call word ptr ds:[0] ;call指令+word ptr作用:将下一个指令偏移地址入栈,跳转到ds:[0] s1: pop ax ;将栈顶出栈到ax call dword ptr ds:[2] ;call+dword ptr作用:将下一指令段地址、偏移地址依次入栈,跳转到ds:[2] s2: pop bx pop cx mov ah, 4ch ;ah==4ch作用为带返回值al结束 int 21h ;进入中断程序 code ends end start
实验结果
Line31的偏移地址为0028
回答问题
①根据call指令的跳转原理,先从理论上分析,程序执行到退出(line31)之前,寄存器(ax) = 0021h 寄存器(bx) = 0026h 寄存器(cx) = 076ch
② 对源程序进行汇编、链接,得到可执行程序task2.exe。以下为反汇编截图
实验任务3
task3.asm
assume ds:data, cs:code, ss:stack data segment x db 99, 72, 85, 63, 89, 97, 55 len equ $ - x ;$表偏移量 此处为7 data ends stack segment dw 16 dup(0) ;定义一个容量为16字节的栈段 初始量为0 stack ends code segment start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, 32 mov cx, len ;数据部分为db,所以len为数据个数(7) print: ;打印数字 mov ah, 0 ;数据只有一个字节,把ah置0,以ax作为被除数 mov al, byte ptr ds:[di] ;把数据放入al inc di ;di指针后移 push cx ;把cx保存起来, call printNumber ;打印数字 call printSpace ;打印空格 pop cx ;弹出cx loop print mov ah, 4ch ;ah==4ch作用为带返回值al结束 int 21h ;进入中断程序 ;子程序: printNumber 功能: 打印数字 ;入口参数: ;寄存器ax (待输出的数据 --> ax) ;局部变量说明: ;bx -> 存储数字字符个数 printNumber: mov bx, 0 ;获取之前位数为0 逐位获取数字 ; getNumber获取每一位压入栈中 getNumber: mov dl, 10 div dl ;数据除10 push ax ;将数字压入栈中(ah余数在ax里) inc bx ;位数+1 mov ah, 0 ;ah是余数,置0后ax表示除法的结果 mov cx, ax ;除法结果赋给cx, 如果结果为0则说明所有位数都获取完了 inc cx ;防止出现负数 loop getNumber ; 打印数字 mov cx, bx ;把bx存的数字位数赋给cx ; printNumberr依次从栈中取出数字,逐位进行打印 printNumberr: pop ax ; 取出一位数 add ah, 30h ; ah是除法的余数,即需要得到的位数,+30h是转成对应字符 mov dl, ah ; 放到dl, 用于打印 mov ah, 2 ; 调用int 21h的2号子程序打印:显示输出dl int 21h loop printNumberr ret ;printSpace打印空格 printSpace: mov ah, 2 mov dl, 20h int 21h ret code ends end start
实验结果
实验任务4
task4.asm
assume cs:code, ds:data data segment str db 'try', 0 data ends code segment start: mov ax, data mov ds, ax mov si, offset str ;将str的偏移量移到si中 mov al, 2 call printStr mov ah, 4ch int 21h ;功能:在指定行、以指定颜色,在屏幕上显示字符串 ;入口参数 ;字符串首字符地址 --> ds:si(其中,字符串所在段的段地址—> ds, 字符串起始地址的偏移地址—> si) ;字符串长度 --> cx ;字符串颜色 --> bl ;指定行 --> bh (取值:0 ~24) printStr: push bx push cx push si push di mov bx, 0b800H mov es, bx mov di, 0 s: mov cl, [si] mov ch, 0 jcxz qaq mov ch, al mov es:[di], cx inc si add di, 2 jmp s qaq:pop di pop si pop cx pop bx ret code ends end start
实验结果
实验任务5
task5.asm
assume cs:code, ds:data data segment stu_no db '201983290213' len = $ - stu_no data ends code segment start: mov ax, data mov ds, ax mov di, 0 ;数据处理 call printStuNum ;调用打印子程序 mov ah, 4ch ;21h的4ch子程序:带返回码al结束 int 21h ;打印子程序: ;参数说明: ;学号字符串存储在 -> ds:[di] printStuNum: mov ax, 0b800h mov es, ax ;控制显存区域段指针 mov si, 1 ;显存区域列指针 ;把屏幕前24行背景打印成蓝色 mov al, 24 ;前24行 mov dl, 80 ;每行80个字符需要修改颜色 mul dl ;24*80, 获得需要填充蓝色的字符数 mov cx, ax Blue: mov al, 17h ;蓝底+白字:0 001 0 111 -> 17h mov es:[si], al ;把颜色填充到位 add si, 2 ;后移2个 loop Blue sub si, 1 ;指针回退一个, 从最后一行起始位置开始 ;打印最后一行 mov ax, 80 sub ax, len ;80列 - 学号长度 mov dl, 2 div dl ;(80 - len)/2, 学号左右两侧需要填充'-'的长度 mov dx, ax ;dx保存'-'的长度 ;调用打印'-'的子程序, 打印左侧的'-' mov cx, dx call printgang ;打印学号 mov cx, len printNumber: mov al, ds:[di] ;低位是字符 mov ah, 17h ;高位是颜色 mov word ptr es:[si], ax ;按字放入 inc di add si, 2 loop printNumber ;调用打印'-'的子程序, 打印右侧'-' mov cx, dx call printgang ret ;子程序: 打印分隔符'-' ;参数: 长度 -> cx ;位置 -> es:[si] printgang: mov al, '-' mov ah, 17h mov word ptr es:[si], ax add si, 2 loop printgang ret code ends end start
实验结果