实验3 转移指令跳转原理及其简单应用编程
实验任务1
task1.asm源码:
assume cs:code, ds:data data segment x db 1, 9, 3 len1 equ $ - x y dw 1, 9, 3 len2 equ $ - y data ends code segment start: mov ax, data mov ds, ax mov si, offset x mov cx, len1 mov ah, 2 s1:mov dl, [si] or dl, 30h int 21h mov dl, ' ' int 21h inc si loop s1 mov ah, 2 mov dl, 0ah int 21h mov si, offset y mov cx, len2/2 mov ah, 2 s2:mov dx, [si] or dl, 30h int 21h mov dl, ' ' int 21h add si, 2 loop s2 mov ah, 4ch int 21h code ends end start
通过debug反汇编可以看到,跳转的位移量为F2h(16进制有符号数补码),转换为十进制为-14;
loop指令是段内转移,只修改IP;loop在指令中给出位移量,通过位移量实现跳转,且位移量的范围是-128~127;
本例中跳转的目的地址由loop下条指令的起始地址+偏移量得到,即0D=1B+F2(十六进制补码)或13=27+(-14)(十进制)
debug反汇编看到跳转的位移量为F0h,转换为十进制为-16;
本例中loop跳转的偏移地址同样由下条指令起始地址与偏移量相加得到,即29=39+F0(十六进制补码)或41=57+(-16)(十进制)
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 mov word ptr ds:[2], offset s2 mov ds:[4], cs mov ax, stack mov ss, ax mov sp, 16 call word ptr ds:[0] s1: pop ax call dword ptr ds:[2] s2: pop bx pop cx mov ah, 4ch int 21h code ends end start
代码分析:
line16和line17 分别将s1处偏移地址存入ds:[0]和将s2处偏移地址存入ds:[2];
line24 call指令将ds:[0]处的字数据作为跳转的偏移地址,在跳转之前将当前的IP即line25的偏移地址压栈;
line25 pop指令弹出的即是line25指令的偏移地址,即(ax)=指令(pop ax)的偏移地址
line27 同理,call将ds:[2]处的双字分别作为跳转的段地址和偏移地址,跳转前将当前的即line28的CS、IP依次压栈;
line28 pop指令先弹出后入栈的IP,即(bx)=指令(pop bx)的偏移地址
line29 pop后弹出先入栈的CS,即(cx)=指令(pop bx)的段地址
g命令执行到0028:
可以看到,ax=0021(pop ax的偏移地址),bx=0026(pop bx的偏移地址),cx=076C(pop bx的段地址)
,和问题①的结论一致。
实验任务3:
task3.asm源码:
assume cs:code, ds:data data segment x db 99,72,85,63,89,97,55 len equ $ - x ;len = 7 data ends code segment start: mov ax, data mov ds, ax mov si, 0 mov bl, 0ah ;除数为10 mov cx, len ;7个数,循环7次 s: mov al, [si] ;将十六进制数移入ax作为被除数 mov ah, 0 call printNumber ;进入转十进制子程序 call printSpace ;进入打印空格子长徐 inc si ;下一个数 loop s jmp exit ;循环结束,退出程序 printNumber: ;子程序,十六进制转十进制并输出 div bl ;除法 mov dl, ah ;商放入dl,准备输出 mov dh, al add dl, 30h ;转换成对应数字的ASCII码 mov ah, 2 int 21h ;输出商 mov dl, dh add dl, 30h int 21h ;输出余数 ret printSpace: ;子程序,输出一个空格 mov dl, ' ' int 21h ;输出空格 ret exit: ;退出程序 mov ah, 4ch int 21h code ends end start
分析:
目标1字节十六进制转换成十进制输出;
用16位数除以0ah,得到的商和余数分别为十进制数的十位和个位;
将数据段中的每个十六进制数(8位)移入al,0移入ah,ax即为16位被除数;除数0ah移入bl,通过div bl得到商和余数;
得到的商和余数加上30h得到对应数字的ASCII码,通过int 21h输出;
对于每个16进制数,循环以上步骤
运行结果:
实验任务4:
task4.asm源码:
assume cs:code, ds:data data segment str db 'try' len equ $ - str data ends code segment start: mov ax, data mov ds, ax mov si, 0 ;数据段指针,指向try mov cx, len ;几个字符循环几次 mov bl, 02h ;颜色 mov bh, 0 ;行位置 mov ax, 0b800h mov es, ax ;显示缓冲区段地址 mov di, 0 ;显示缓冲区指针 mov al, 0a0h ;每行80个字符,每个字符2字节,每行160字节,用来确定行首 call printStr ;调用子程序 mov si, 0 mov di, 0 mov bl, 04h mov bh, 18h mov ax, 0b800h mov es, ax mov cx, len mov al, 0a0h call printStr jmp exit printStr: mul bh ;确定行首在显示缓冲区的位置 add di, ax s: mov al, [si] mov es:[di], al ;移入字符ASCII码 mov al, bl mov es:[di+1], al ;移入属性(颜色) inc si ;try指针+1,指向下个字符 add di, 2 ;每个显示的字符占2字节,显示缓冲区指针+2 loop s ret exit: mov ah, 4ch int 21h code ends end start
分析:
将try的各个字符与设置的颜色依次交替放入显示缓冲区,形式如:'t' 02 'r' 02 'y' 02
分别用si和di保存数据和显示缓冲区的当前位置;
通过 a0h×行 确定行首位置,再依次将字符ASCII码、颜色移入显示缓冲区
运行结果:
实验任务5
task5.asm源码:
assume cs:code, ds:data data segment stu_no db '201983290219' len = $ - stu_no data ends code segment start: mov ax, data mov ds, ax mov ax, 0b800h mov es, ax mov bl, 017h ;字符的属性,蓝底白字 mov si, 0 ;指向数据段的数据 mov di, 0 ;指向显示缓冲区位置 mov al, 050h ;每行80个字符 mov bh, 18h ;0~23行只修改属性,不修改ASCII码 mul bh mov cx, ax ;只修改背景色的部分 s: mov es:[di+1], bl add di, 2 loop s mov cx, 022h ;最后一行前半部分的-- s1: mov byte ptr es:[di], '-' mov es:[di+1], bl add di, 2 loop s1 mov cx, len ;最后一行的学号 s2: mov al, [si] mov es:[di], al mov es:[di+1], bl inc si add di, 2 loop s2 mov cx, 023h ;学号后面的-- s3: mov byte ptr es:[di], '-' mov es:[di+1], bl add di, 2 loop s3 code ends end start
分析:
0~23行只修改字符的属性为蓝底白字,共循环24行*80个字符次;
最后一行为34个'-' + 12位学号 + 35个'-' ,通过循环写入;
运行结果: