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

实验任务1

本任务是帮助我们理解运算符offset、伪指令equ、预定义符号$的灵活使用。
经过反汇编可以得知,offset后接偏移地址,在程序加载的后已经对其进行定位。equ是等于的意思。预定义符号 $不占代码内存。

点击查看代码
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;转换成对应的ASCII码
    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

运行后的截图:
image

回答问题:
① line27, 汇编指令 loop s1 跳转时,是根据位移量跳转的。通过debug反汇编,查看到机器码为E2F2,其中E2对应了Loop的机器码,那么可以判断出F2就是他的位移量,通过上课所学到的知识以及查阅相关资料, F2是以补码的形式存储的,将其转换为二进制就是1000 1110,即十进制的-14
从CPU角度去看,首先查看loop后面偏移地址为000D,在看下一条指令开始地址为001B,那么由000D-001B=-000E,换算成十进制就是-14
debug反汇编截图:
image
② line44,汇编指令 loop s2 跳转时,是根据位移量跳转的。分析的过程与上述类似,这里直接给出答案,偏移量是16,由0029h-0039h得到。并且观察机器码中F0,也满足该答案。
image

实验任务2

本任务旨在对call指令原理的理解。

点击查看代码
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
① 根据call指令的跳转原理,先从理论上分析,得到三个寄存器中的内容。

在执行到这条指令的时候,源码中由两个call指令,我们逐条分析,首先观察第一条call,这里的call指令是转到s1处,同时目标偏移量大小为word,那么把下一条指令的起始地址的ip压入栈。而与之相对应的是第二个call指令处,目标偏移量是dword,因此第二个call指令是把下一条指令地址的csip依次压入栈中,所有看到s1s2后面的pop指令不难得到,ax=第一个的ipbx=第二的ipcx=第二的cs。其对应内容为下截图:

image

理论分析完成,用debug调试的结果如下:image
符合分析的结果!

实验任务3

要求是将data段中的数据以十进制输出。逻辑段如下:

data segment
	x db 99, 72, 85, 63, 89, 97, 55
	len equ $- x
data ends

此外输出单个字符的命令为:

	mov ah, 2
	mov dl, ×× ; ××是待输出的字符,或其ASCⅡ码值
	int 21h

观察可以发现,这几组数都是两位数,当尝试将其直接输出发现,存储在对应内存单元的都是十六进制,不能满足题意,相关截图丢失,可以根据这段内存自己脑补。

image
那么本题就是需要将这些两位数拆分开来,在把每个字符转换成ASCII码输出,相关代码如下:

点击查看代码
assume cs:code, ds:data
data segment
	x db 99, 72, 85, 63, 89, 97, 55
	len equ $- x
data ends

code segment
start:
	mov ax, data
	mov ds, ax
	mov si, offset x
	mov cx, len
	
s:	call printNumber
	call printSpace
	loop s
	
	mov ah, 4ch
	int 21h
printNumber:
	mov ah, 0
	mov al, [si]
        mov bl, 10
	div bl
	mov bx, ax
	
	mov ah, 2
	mov dl, bl
	or dl, 30h
	int 21h
	
	mov dl, bh
	or dl, 30h
    int 21h
	inc si
	
	ret
	
printSpace:
	mov ah, 2
	mov dl, ' '
    int 21h
	ret
code ends
end start
主要考察的是对`div`命令的应用,以及将字符转换为ASCII码。 运行后的截图如下:

image

此外值得思考的是,是否有方法能将这些数字转换为字符串,这样无论字数的多少,都可以对其进行输出,这也是博主最初的解题思路,但是实现的过程有一些复杂,在查阅了部分资料之后就转换了方法。希望在今后深入学习后能掌握相关方法。

实验任务4

将如下数据段以要求的形式输出在屏幕上

data segment
	str db 'try'
	len equ $ - str
data ends

要求是分别在左上角和左下角以绿色和红色输出这段字符。
分析:首先可以根据要求知道需要编写相关子程序,入口参数有字符串首地址,字符串长度,字符串长度,字符串颜色,指定行。
我们逐一解决,字符串的首地址可以用offsetstr存储在si寄存器中,字符串长度也很简单,根据定义知道就是len的大小,指定行根据范围可以知道,首行bh=0,尾行bh=24,最后确定其颜色,根据书本上所学到的:
image
那么黑底绿字应该是00000010b,即十进制中的2,黑底红色就是00000100b,即十进制中的4.
剩下的问题就是如何定位了,下面代码将展示:

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 ax, 0b800h
	mov es, ax;显存位置
	
	mov cx, len
	mov si, offset str 
	mov bh, 0
	mov bl, 2
	call printStr
	
	mov cx, len
	mov si, offset str 
	mov bh, 24
	mov bl, 4
	call printStr
	
	mov ah, 4ch
	int 21h

; 子程序: printStr
;    功能: 在指定行、以指定颜色,在屏幕上显示字符串 
;    入口参数:
;       字符串首字符地址 --> ds:si (其中,字符串所在段的段地址—> ds, 字符串起始地址的偏移地址—> si)
;       字符串长度      --> cx
;       字符串颜色      --> bl
;       指定行         --> bh (取值:0 ~ 24)	
printStr:
	
	mov al, bh;计算起始地址放在di中
	mov dl, 0a0h
	mul dl
	mov di, ax
	
	mov ah, bl
s:	mov al, [si]
	mov es:[di], ax
	add di, 2
	inc si
	loop s
	ret
	
code ends
end start

根据代码可以知道,这里是直接将对应行的地址计算出来,用到了mul指令,已知有25行,每行有80个字符,输出的形式是以对应字符+属性,所有在ah中存属性,al中存内容,在将ax存放在对应显存位置即可。
最终效果如下:
image

符合题意,主要考察的就是对各种地址的合理运用,并且在题目已经对一些数据进行要求的情况下,如何写好程序。

实验任务5

在80×25彩色字符模式下,在屏幕最后一行正中间显示学号。要求输出窗口蓝底,学号和两侧折线,以
白色前景色显示。
逻辑段为:

data segment
  stu_no db '201983290487'
  len = $ - stu_no
data ends

本题可以将任务分为两部分,首先将整个页面设置为蓝底白字,再在最后一行输出要求的格式。相关代码如下:

assume cs:code, ds:data

data segment
  stu_no db '201983290487'
  len = $ - stu_no
data ends

code segment
start:
	mov ax, data
	mov ds, ax
	mov ax, 0b800h
	mov es, ax

	mov bh, 0
	mov al, bh;计算起始地址放在di中
	mov dl, 0a0h
	mul dl
	mov di, ax
	
	mov ax, 24;计算需要重复多少次可以将屏幕变成蓝底白字
	mov dl, 80
	mul dl
	
	mov cx, ax
s1:	mov al, ' '
	mov ah, 17h;蓝底白字
	mov es:[di], ax
	add di, 2
	loop s1

	mov si, offset stu_no
	mov bh, 24
	mov al, bh;计算起始地址放在di中
	mov dl, 0a0h
	mul dl
	
	mov di, ax
	call printStr

	mov ax, 4c00h
	int 21h

printStr:
	call printline

	mov cx, len
s2:	mov ah, 17h;蓝底白字
	mov al, [si]
	mov es:[di], ax
	inc si
	add di, 2
	loop s2

	call printline
	ret

printline:
	mov cx, 34;(80-12)/2
s3:	mov ah, 17h
	mov al, '-'
	mov es:[di], ax
	add di, 2
	loop s3
	ret

code ends
end start

本题与实验任务4有共通之处,只是在任务4的基础上,对子程序的调用以及程序执行的次数等计算上提高了要求。
运行结果如下:
image
满足要求!


实验总结

本次实验已经对一些相对实际的任务有了要求,也越来越接近实际的编程。同时在写一些题目时也有收获和思考,在处理实验三时,我的第一想法是直接将这些数据输出,发现输出的16进制的ASCII码对应内容,我又在思考能否直接将这些数转换成字符串,这样也便于输出,但是在查阅了相关资料,也是了解到复杂性,偏离了本任务的考察要求,但是在之后的学习,希望能逐渐掌握这些方法。
随着实验的一次次深入,我们的实验任务以及不满足于对于命令的使用或者是对地址的判断,寄存器的使用,而是在此基础上,尝试去实现一些复杂的任务,将我们所学到的知识应用到实际中去,过程中难免遇到一些困难,但是与同学交流或者查阅资料都是解决方法。问题的一次次迎刃而解都是收获。

posted @ 2021-11-27 21:02  斯文小扬  阅读(144)  评论(0编辑  收藏  举报