实验3 转移指令跳转原理及其简单应用编程


实验任务一

代码:

 1 assume cs:code, ds:data
 2 data segment
 3 x db 1, 9, 3
 4 len1 equ $ - x ; 符号常量, $指下一个数据项的偏移地址,这个示例中,是3
 5 y dw 1, 9, 3
 6 len2 equ $ - y ; 符号常量, $指下一个数据项的偏移地址,这个示例中,是9
 7 data ends
 8 code segment
 9 start:
10 mov ax, data
11 mov ds, ax
12 mov si, offset x ; 取符号x对应的偏移地址0 -> si
13 mov cx, len1 ; 从符号x开始的连续字节数据项个数 -> cx
14 mov ah, 2
15 s1:mov dl, [si]
16 or dl, 30h
17 int 21h
18 mov dl, ' '
19 int 21h ; 输出空格
20 inc si
21 loop s1
22 mov ah, 2
23 mov dl, 0ah
24 int 21h ; 换行
25 mov si, offset y ; 取符号y对应的偏移地址3 -> si
26 mov cx, len2/2 ; 从符号y开始的连续字数据项个数 -> cx
27 mov ah, 2
28 s2:mov dx, [si]
29 or dl, 30h
30 int 21h
31 mov dl, ' '
32 int 21h ; 输出空格
33 add si, 2
34 loop s2
35 mov ah, 4ch
36 int 21h
37 code ends
38 end start

1. 理解运算符offset、伪指令equ、预定义符号$的灵活使用。

答:

运行结果如下:

offset的作用是取得标号的偏移地址。如本题定义一个数组x含有三个元素,则通过offset x可以取得x的起始地址,也就是0.

len1 equ相当于定义一个常量,常量名为equ,对应偏移地址为$-x相当于对应x最后一个元素的偏移量,也就是3.

2. 回答问题

① line27, 汇编指令 loop s1 跳转时,是根据位移量跳转的。通过debug反汇编,查看其机器码, 分析其跳转的位移量是多少?(位移量数值以十进制数值回答)从CPU的角度,说明是如何计算得 到跳转后标号s1其后指令的偏移地址的。

答:反汇编结果如下

 跳转的位移量是1B-D=E=14

CPU首先取得目前正在执行的指令的地址加上本条指令所占用空间的地址,也就是001B,然后减去标号的地址000D得到结果E,然后向前移动E个单位。

 

② line44,汇编指令 loop s2 跳转时,是根据位移量跳转的。通过debug反汇编,查看其机器码, 分析其跳转的位移量是多少?(位移量数值以十进制数值回答)从CPU的角度,说明是如何计算得 到跳转后标号s2其后指令的偏移地址的。

答:反汇编结果如下。

 跳转的位移量是39-29=10=16

CPU首先取得目前正在执行的指令的地址加上本条指令所占用空间的地址,也就是0039,然后减去标号的地址0029得到结果10h,然后向前移动10h个单位。

 

③ 附上上述分析时,在debug中进行调试观察的反汇编截图

 

实验任务二

 代码:

 1 assume cs:code, ds:data
 2 data segment
 3 dw 200h, 0h, 230h, 0h
 4 data ends
 5 stack segment
 6 db 16 dup(0)
 7 stack ends
 8 code segment
 9 start:
10 mov ax, data
11 mov ds, ax
12 mov word ptr ds:[0], offset s1
13 mov word ptr ds:[2], offset s2
14 mov ds:[4], cs
15 mov ax, stack
16 mov ss, ax
17 mov sp, 16
18 call word ptr ds:[0]
19 s1: pop ax
20 call dword ptr ds:[2]
21 s2: pop bx
22 pop cx
23 mov ah, 4ch
24 int 21h
25 code ends

① 根据call指令的跳转原理,先从理论上分析,程序执行到退出(line31)之前,寄存器(ax) = ? 寄存器 (bx) = ? 寄存器(cx) = ?

答:ax中值应该是s1的偏移地址,bx中值应该是s2的偏移地址,cx中值应该是cs存储的地址。

首先ds:[0]指向的地址应该是s1的偏移地址,则call word ptr ds:[0]就是跳转到ds:[0]指向的地址执行,并且将当前IP压入栈中,当前的IP正好也是s1的偏移地址,所以ax对应s1指令的偏移地址。

bx是call dword ptr,所以会把cs和IP都压入栈中,之后同理,因为下一条就是s2,执行pop bx会把s2的地址出栈,存储给bx。

对于cx,由于栈中执行的操作是三次进栈,两次出栈操作,所以栈中还存储有cs的地址,出栈后cx对应栈中的地址。

 

② 对源程序进行汇编、链接,得到可执行程序task2.exe。使用debug调试,观察、验证调试结果与理论 分析结果是否一致。

 

 

 

实验任务三

要求: 编写子程序printNumber

功能:以十进制形式输出一个两位数

入口参数:寄存器ax(待输出的数据 --> ax)

出口参数:无 编写子程序

printSpace 功能:打印一个空格

入口参数:无

出口参数:无

在主体代码中,综合应用寻址方式和循环,调用printNumber和printSpace, 实现题目要求。

 

代码:

 1 assume ds:data, cs:code
 2 
 3 data segment
 4     x db 99, 72, 85, 63, 89, 97, 55
 5     len equ $- x
 6 data ends
 7 
 8 code segment
 9 start:
10     mov ax,data
11     mov ds,ax
12     mov si,0
13     mov cx,len;cx中存储定义的字的个数,这也是循环会执行的次数
14     s:  call printNumber;打印一个两位数
15     call printSpace;打印一个空格
16     inc si
17     loop s
18 
19     mov ax,4c00h
20     int 21h
21 
22     printNumber:
23     mov ah, 0
24     mov al, [si]
25         mov bl, 10
26     div bl
27     mov bl,al
28     mov bh,ah
29 
30     mov ah,2
31     mov dl,bl
32     add dl,30h
33     int 21h
34 
35     mov ah,2
36     mov dl,bh
37     add dl,30h
38     int 21h
39     ret
40 
41 
42     printSpace:
43     mov ah,2
44     mov dl,' '
45     int 21h
46     ret
47 code ends
48 end start

效果:

 

 

实验任务四

要求: 编写子程序printStr

功能:

在指定行、以指定颜色,在屏幕上显示字符串

入口参数

字符串首字符地址 --> ds:si(其中,字符串所在段的段地址—> ds, 字符串起始地址的偏 移地址—> si)

字符串长度 --> cx

字符串颜色 --> bl

指定行 --> bh (取值:0 ~24)

出口参数:无

 

代码:

 1 assume ds:data, cs:code
 2 
 3 data segment
 4 str db 'try'
 5 len equ $ - str
 6 data ends
 7 
 8 code segment
 9 start:
10     mov ax,data
11     mov ds,ax
12 
13     mov si,0
14     mov cx,len;字符串长度为len赋值给cx
15     mov bh,0
16     mov bl,00000010B;对应绿色
17     call printStr
18     
19     mov si,0
20     mov cx,len;字符串长度为len赋值给cx
21     mov bh,24
22     mov bl,00000100B;对应红色
23     call printStr
24 
25     mov ax,4c00h
26     int 21h
27     
28 
29     printStr:
30     mov ax,0b800h;设置ax为显存段地址
31     mov es,ax
32 
33     mov al,bh
34     mov bh,0a0h
35     mul bh
36     mov bp,ax
37     s:
38     mov ah,ds:[si];每次取出一个字节给ah
39     mov es:[bp],ah;把字母存给显存地址低八位中
40     inc bp
41     mov es:[bp],bl;将颜色信息存入高八位
42     inc bp
43     inc si
44     loop s
45     ret
46 
47 code ends
48 end start

效果:

 

 

实验任务五

代码:

 1 assume ds:data, cs:code
 2 
 3 data segment
 4 stu_no db '20498329042'
 5 len = $ - stu_no
 6 data ends
 7 
 8 code segment
 9 start:
10     mov ax,0b800h
11     mov es,ax
12     mov bp,1
13     mov cx,8000h
14 
15     first:
16     mov byte ptr es:[bp],00011111B
17     add bp,2
18     loop first
19 
20     mov ax,data
21     mov ds,ax;ds作数据段
22     mov ax,0050h;一行的长度
23     sub ax,len;减去学号的长度
24     mov bh,02h;除以二得到横线的长度
25     div bh
26     mov bl,al;商赋给bx,后面要用
27     mov bh,0
28 
29     mov ax,0b800h;显存地址存给es
30     mov es,ax
31     mov bp,0F00h;首字母偏移量赋给bp
32     mov cx,bx
33 
34     s:
35     mov byte ptr es:[bp],'-'
36     inc bp
37     inc bp
38     loop s;打印横杠
39 
40     mov cx,len
41     mov di,0
42 
43     s1:
44     mov al,ds:[di]
45     mov es:[bp],al
46     inc bp
47     inc bp
48     inc di
49     loop s1;打印学号
50 
51     mov cx,bx
52 
53     s2:
54     mov byte ptr es:[bp],'-'
55     inc bp
56     inc bp
57     loop  s2;打印横杠
58 
59     mov ax,4c00h
60     int 21h
61 
62 code ends
63 end start

效果:

 

实验总结:

本次实验主要考察了两部分的内容,一部分是对跳转指令的理解和应用,另一部分是对显存数据写入的理解和应用。

首先实验使用了equ指令定义常量len,这个常量因为是定义字符的下一个地址,字符的开始地址是从0开始的,所以就可以得到字符的长度。

CPU执行跳转操作的时候,不是直接根据所给的地址进行跳转,而是根据下一条指令的偏移量和给出的目标地址进行计算得到位移量进行回退的。执行call命令的时候需要利用栈来存储跳跃前的指令的地址,这样在执行ret的时候就可以快速跳转回去,所以在此期间也不可以对栈段和栈偏移地址寄存器进行修改。offset指令通过获得某个定义的代码块的偏移地址来达成和call类似的效果。

将字符显示在屏幕上有两种方法,第一种是通过int21h的二号指令,将想要打印的数传给al来实现,因为会打印ASCII码,所以对于打印数字来说需要进行一次or 30h或add 30h的运算,才可以将数字转换成对应的码值。

写入显存的时候需要知道显存一行所占的字节数,显存地址从0b800h开始,偏移000-09F对应第一行,总计24行以此类推。对于每一个字来说,低位存储了想要打印的数字或字符,高位决定了数字的前景色和背景色,前四位决定背景色和高亮,后四位决定前景色和高亮,因此在显存中打印字符的时候需要一次跳两位。如果想像本题最后一题那样把整个屏幕都变蓝,我采取的方法是在开始的时候就把蓝色背景色对应数字存入每一个单数的数字位中(高位),这样整个屏幕就变蓝了。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM