转移指令的原理

1、分析一个奇怪的程序(课本实验8

题目:分析下面的程序,在运行前思考:这个程序可以正确返回吗?

运行后再思考:为什么是这种结果?

通过这个程序加深对相关内容的理解。

assume cs:code

code segment

mov ax,4c00H

int 21H

start:

    mov ax,0

    s:

nop

nop

mov di,offset s

mov si,offset s2

mov ax,cs:[si]

mov cs:[di],ax

s0:jmp short s

s1:

mov ax,0

int 21H

mov ax,0

s2:

jmp short s1

nop

code ends

end start

解:先遵循正规的套路,先从start开始:

由start一直往下:

直到:

mov di, offset s

mov si, offset s2

mov ax, cs:[si]

mov cs:[di], ax

这个的意思是将s2处的第一个字节开始的代码(直接理解为那一行的代码)移动到s处。

cs:[di]就得到了ax的信息,就是说标号s处将得到ax的信息。

下面就执行到:s0: jmp short s

这时候,s处得到的值已经不再是以前的值了。

同时,也不再是跳回到s1处的意思了。

jmp short si ; 这句 jmp short s1 , 根据我们之前的分析 , 指令是用相对偏移来表示的

; 因此执行的操作并不是真的跳转到 s1 这个标号 , 而是跳转编译时确定的 该指令到 s1 标号的偏移

; 所以我们要分析接下来程序的流程的话 , 就必须先编译程序 , 然后要知道到底偏移是多少

; 然后再根据这个偏移确定程序下一步应该执行哪里的指令

课本之前说过,jmp指令对应的机器码前两位表示的是向前还是向后,后两句代表的是位移(位移=标号处的偏移地址-jmp后第一个字节的偏移地址),所以ax得到的不是jmp short s1这句代码,而是得到的一个四位(两个字节)的数据,表示的意义是 “向前"的”[s1]-[s2]"

查看机器码,

也可看出:

偏移是 : EB F6

其中 EB 表示的是跳转 , F6 表示偏移

F6 怎么理解呢 ?

1111 0110 (使用补码来表示)

补码转换成原码 , 符号位不变 , 其他位取反 , 然后加 1

1000 1001

1000 1010 ; 也就是 -10

也就是上面我们分析的让 (ip) = (ip) - 0x0A

然后 , 这句指令被复制到 s 标号的开头处

由于 nop 只占一个字节 , 因此两个 nop 被完全替代

然后程序执行到 s0 , 又跳转到 s 开始的地方

这个时候就要执行 : (这个时候 ip = 8)

EB F6

首先读取这条指令到指令缓存器里

接下来 , (ip) = (ip) + len(EB F6) = (ip) + 8 = 10

然后执行这条指令 , 即为 (ip) = (ip) - 10 = 0

这样 ip 就回到了 code segment 的起始处

这样继续执行

mov ax, 4C00H

int 21H

就实现了程序的正常返回

  1. 根据材料编程(课本实验9

题目:

解答:

首先书中介绍了 80 x 25 彩色字符模式的显示缓冲区。

题目的要求是在第据此,若想大致的将字符显示在显存中间,需要稍微进行计算:

行:一共25行,25 / 2 = 12行(向下取整)

列:要显示的字符串 ‘welcome to masm!’ 一共16字节,每一行可以显示160字节,所以第一个元素 w 的位置: 第(160 - 16) / 2 = 72字节。

可以计算得到首个字符在显示区中的偏移:

第 12 * 160 + 72 = 1992 = 07C8H 字节处。

下面为源代码:

assume cs:code, ds:data

data segment

db 'welcome to masm!'   

 ;将待打印的字符串放到数据段,为后续拷贝到显存处做准备

data ends

code segment

start: mov ax, data         

mov ds, ax       ;ds字符串区

mov ax, 0b800h     ;显存地址

mov es, ax     ;显存段基址

;在汇编中表示地址不可以以字母起始,所以需要添上一个0 -> 0B800H。

mov cx, 16 ;16个字母,拷贝十六次

mov si, 0 ;显存偏移

mov di, 12 * 160 + 80;显存首字符放置处

s: mov al, ds:[si]

mov ah, 01110001B ;白底蓝字

mov es:[di], ax

inc si

add di, 2

loop s

mov cx, 16     ;循环拷贝16次

mov si, 0       ;字符串区偏移

mov di, 13 * 160 + 80    ;找到中间位置

s0: mov al, ds:[si]

mov ah, 11000010B    ;设置字体属性: 白底蓝字

mov es:[di], ax         ;写入到显存

inc si                  ;字符串向后偏移1

add di, 2         ;显存写入位置向后偏移两个字节

loop s0

mov cx, 16                    ;后面开始循环

mov si, 0

mov di, 14 * 160 + 80

s1: mov al, ds:[si]

mov ah, 00000111B

mov es:[di], ax

inc si

add di, 2

loop s1

mov ax, 4c00h

int 21h

code ends

end start

运行效果:

3、有个sc1的字节数组,长度为32,请统计其中0的个数,并将统计结果存入zero中。

sc1 db 12,18,45,0,56, ……

Zero db ?

解:DATAS SEGMENT

    sc1 db 12,18,45,0,56,9 dup(0,1,0)  ;统计19个0

    Zero db 0

DATAS ENDS

STACKS SEGMENT

    db 16 dup(0)   ;空出栈段

STACKS ENDS

CODES SEGMENT

    ASSUME CS:CODES,DS:DATAS,SS:STACKS

START:

    MOV AX,DATAS

    MOV DS,AX

    mov ax,stacks

    mov ss,ax

    mov sp,17    ;设置栈段和数据段

    mov bx,0

    

    mov ax,0     ;用ax来计数

    mov cx,32    ;数组长度32字节

  s:push cx

    mov ch,0     ;cx高位置0

    mov cl,[bx]

    inc bx

    jcxz s1      ;找到字节为0就跳转s1,否则弹出循环次数并减一

 s2:pop cx

    loop s

    

    mov word ptr [Zero],ax

    MOV AH,4CH

    INT 21H

    

 s1:inc ax     ;找到0要计数加1,跳转s2

    jmp s2

CODES ENDS

END START

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值