0 0 0

科锐学习笔记-第三阶段-16位汇编 06 串操作和流程转移指令

admin
260 0

本文共计11379个字,预计阅读时长45.5分钟。

目录

串操作

串操作

源操作数使用si,默认段为DS,可段超越

目的操作数使用di,默认段为ES,不可段超越

img

串方向

串方向由DF标志位影响,可以由指令修改

img

重复前缀

串操作指令一般都配合重复前缀使用,实现内存的批量操作。

img

流程转移指令

1.无条件跳转

直接转移 jmp

img

短跳 jmp 或者 jmp short

偏移值范围是 - 128(80H) 到 127(7FH) , 偏移值大小是 一个字节

;栈段 stack segment stack stack ends   ;数据段 data segment   g_szBuf db 255 dup(0) data ends   ;代码段 code segment START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,ax   mov ax,ax   jmp   LEABEL1  ; 跳转到标号 LEABEL1   mov ax,ax   mov ax,ax LEABEL1:     mov dx,dx   mov dx,dx   jmp   LEABEL2  ; 跳转到标号 LEABEL2   mov dx,dx LEABEL2:       mov cx,cx   mov cx,cx code ends   end START

img

img

ip 里面存的是下一条指令的地址. ip被改了,代表下一条指令 也被修改了

img

jum 既然能跳转,那么指令肯定包含了 要跳转的地址信息 ,那么是如果做到的呢

开始IP 跳转IP 地址偏移值 机器码
0009 000f 6 EB04
0013 0017 4 EB02

从表格中我们不难分析出来 EB 代表 jmp 指令 ,其机器码中并没有直接存储 目标地址的 ip信息 ,也不是直接存储偏移,而是地址偏移值 - 2 , 即 下一条指令 到 目标地址的偏移,那为什么不存储本条指令地址和目的地址的偏移,而要存下一条的呢,因为当一条指令执行完,IP地址就指向了下一条指令的ip地址,如果要储存自己和目标指令的偏移,需要先 - 2 ,把ip地址 改回当前自己的,再去算偏移,所以就直接存储 下条指令的 ip 地址 和目标地址的偏移

上面是考虑的是 从上往下跳,如果从下往上跳呢

stack segment stack    ;栈段 stack ends   data segment     ;数据段 g_szBuf db 255 dup(0) data ends   code segment    ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,ax   mov ax,ax   jmp   LEABEL1  ; 跳转到标号 LEABEL1   mov ax,ax   mov ax,ax LEABEL1:       mov dx,dx   mov dx,dx   jmp   LEABEL1  ; 跳转到标号 LEABEL1   jmp   LEABEL2  ; 跳转到标号 LEABEL2   mov dx,dx LEABEL2:       mov cx,cx   mov cx,cx code ends   end START

img

开始IP 跳转IP 地址偏移值 机器码
0013 000f -4 EBFA

FA 是 -6 , -4 = -6 +2 ,因此向上跳 也是 记录的 下条指令 到 目标地址的 偏移

近跳 jmp 或者 jmp near

短跳的偏移值 范围 是 - 128(80H) 到 127(7FH) ,如果超过这个范围,那么短跳将无法实现,此时就需要用到近跳

近跳 偏移值范围是 - 32766((8000H)) 到 32765(7FFFH) , 偏移值大小是 一个字

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_szBuf db 255 dup(0) data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,ax   mov ax,ax   jmp   LEABEL1  ; 跳转到标号 LEABEL1,此时跳转范围超过一个字节   mov ax,ax   mov ax,ax   db 255 dup(0)   LEABEL1:       mov dx,dx   mov dx,dx   jmp   LEABEL1  ; 跳转到标号 LEABEL1   jmp   LEABEL2  ; 跳转到标号 LEABEL2   mov dx,dx LEABEL2:       mov cx,cx   mov cx,cx code ends   end START

img

此时 这条指令 占3个字节,偏移值是一个 word ,偏移值 是 0103 (小尾保存的 )

000c (下条指令地址) + 0103 (偏移值) = 010F 及目标地址

远跳 jmp far

上面都是在一个段内跳转,如果在不同段内跳转 ,其偏移值 可以 超过 word 的表达范围,此时就需要用远跳

img

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_szBuf db 255 dup(0) data ends   code1 segment  ;代码段 db 64 dup(0) LEABEL3:     xor ax,ax   xor ax,ax   db 521 dup(0) code1 ends code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,ax   mov ax,ax   ;jmp far ptr LEABEL3  ; 跳转到标号 LEABEL3,此时是跨段跳   jmp code1:LEABEL3    ; jmp 段名:标号名  ,效果跟上面一样     jmp  LEABEL1  ; 跳转到标号 LEABEL1,此时跳转范围超过一个字节   mov ax,ax   mov ax,ax   db 255 dup(0)   LEABEL1:       mov dx,dx   mov dx,dx   jmp   LEABEL1  ; 跳转到标号 LEABEL1   jmp   LEABEL2  ; 跳转到标号 LEABEL2   mov dx,dx LEABEL2:       mov cx,cx   mov cx,cx code ends   end START

img

指令偏移计算

指令中的偏移值是下一条指令到目的地址的偏移。

img

指令偏移计算练习

计算下列指令的机器码,短跳 0xEB,近跳 0xE9,远跳跳 0xEA。

0AE7:0102 jmp 0120 -- EB1C

  • 120 - 104 = 1C 所以机器码是 E91B02

0AE7:0102 jmp 0320 -- E91B02

  • 因为偏移值超过一个字 ,偏移值是字,因此这条指令是 3个字节 ,下条指令地址是 105

320 - 105 = 021B 所以机器码是 E91B02

0AE7:0102 jmp 20 -- E91BFF

  • 104 - 20 = E4 取反是 FF1C 用一个字节无法表示,所以偏移值应该是用一个字表示,那么该条指令应该是3个字节,下条指令地址应该是 105, 105 - 20 = E5 取反 FF1B 所以机器码是 E91BFF

0AE7:0402 jmp 0100 -- E9FBFD

  • 该条指令偏移值明显看出是一个字,所以指令长度3 ,下条指令地址是 405, 405 - 100 = 305 取反 FDFB,所以机器码是 E9FBFD

0AE7:0122 jmp 0110 -- EBEC

  • 124 - 110 = 14 取反是 EC 所以机器码是 EBEC

img

img

img

img

img

使用寄存器间接转移
  • 格式 jmp reg
  • reg 为通用寄存器,不涉及段寄存器
  • 功能 ip <- reg
  • 只能用于段内转移

img

使用EA的间接转移

把跳转地址存到内存, 跳转时 从 内存里取 出跳转的目标地址

img

注意:

在运用转移的时候需要遵循C语言的流程标准,因为类似于goto,不能从一个函数跳到另外一个函数,影响堆栈平衡。

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w dw 0 data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov g_w, offset  LEABEL4 ;将 LEABEL4 的  偏移值给 g_w   jmp g_w     mov ax,ax   xor ax,ax LEABEL4:       ;带返回码的退出   mov ax,4C00H   int 21   code ends   end START

img

img

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w dw 0 g_wEA dw 0 g_wCS dw 0   data ends   code1 segment  ;代码段 db 64 dup(0) LEABEL3:     xor ax,ax   xor ax,ax   db 521 dup(0) code1 ends code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov g_wEA,offset LEABEL3 ;将 LEABEL3 的  偏移值给 g_wEA   ;mov g_wCS,code          ;将 LEABEL3 的 段基址给 g_wCS   mov  g_wCS,seg LEABEL3   ;将 LEABEL3 的  段基址给 g_wCS   lea bx,g_wEA             ;将 g_wEA 的  偏移地址给 bx   jmp dword ptr [bx]       ;取出偏移值为bx (即 g_wEA) 处的值,并跳转到该处     jmp g_w     mov ax,ax   xor ax,ax LEABEL4:       ;带返回码的退出   mov ax,4C00H   int 21   code ends   end START

img

2.条件跳转

依据标志位判断,条件成立则跳转,条件不成立则不跳。

单条件跳转

深色表示常用,需要掌握 ,其他的表是用的比较少,熟悉即可

img

jz/je

比较2个数,相等输出 eaqul 不等输出 not eaqul

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w0 dw 1122H g_w1 dw 3344H g_w2 dw 1122H g_szEqu db "eaqul",0dh,0ah,'$' g_szNEqu db "not eaqul",0dh,0ah,'$' data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,g_w0   ;将 g_w0 的值 给 ax   cmp ax,g_w1   ; ax的值 和 g_w1 作比较   jz  LEABEL2   ; 如果比较的值相等则跳转 LEABEL2 标号处   ;输出字符串  g_szNEqu   lea dx,g_szNEqu   mov ah,09H   int 21H   mov ax,g_w0   ;将 g_w0 的值 给 ax   cmp ax,g_w2   ; ax的值 和 g_w2 作比较   jz  LEABEL2   ; 如果比较的值相等则跳转 LEABEL2 标号处   ;输出字符串  g_szNEqu   lea dx,g_szNEqu   mov ah,09H   int 21H   jmp EXIT LEABEL2:   ;输出字符串  g_szEqu   lea dx,g_szEqu   mov ah,09H   int 21H EXIT:       ;带返回码的退出   mov ax,4C00H   int 21H   code ends   end START

img

img

img

jcxz

判断2个字符串是否相等,相等则输出 eaqul 否则输出 eaqul not

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w0 dw 1122H g_w1 dw 3344H g_w2 dw 1122H g_szEqu db "eaqul",0dh,0ah,'$' g_szNEqu db "eaqul not ",0dh,0ah,'$' data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   cld   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds   mov es,ax        ;不直接给是因为没有立即数到段寄存器  mov cx,offset g_szNEqu - offset g_szEqu  lea si,g_szEqu  lea di,g_szNEqu  repz cmpsb       ;2个字符串进行比较,直到cx等于0,或者不等退出  jcxz LEABEL2    ;如果 cx 等于 0 则跳转,否则不跳转   ;输出字符串  g_szNEqu   lea dx,g_szNEqu   mov ah,09H   int 21H   jmp EXIT LEABEL2:   ;输出字符串  g_szEqu   lea dx,g_szEqu   mov ah,09H   int 21H EXIT:       ;带返回码的退出   mov ax,4C00H   int 21H   code ends   end START

image.png

C:>DEBUG JMP.EXE

-G

EAQUI

NOT

NORMALLY (0000)

PROGRAM TERMINATED

image.png

无符号数判断

image.png

image.png

有符号数判断

image.png

3.LOOP

使用频率很高 只能用于短转移

格式: loop 标号

image.png

image.png

字符串小写转大写,并输出

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w0 dw 1122H g_w1 dw 3344H g_w2 dw 1122H g_sz db "helloworld",0,0dh,0ah,'$' g_szEqu db "eaqul",0dh,0ah,'$' g_szNEqu db "eaqul not ",0dh,0ah,'$' data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   cld   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds   mov es,ax        ;不直接给是因为没有立即数到段寄存器   ;输出字符串  g_szNEqu   lea dx,g_sz   mov ah,09H   int 21H   mov cx,offset g_szEqu - offset g_sz -4 ;去掉\0 和\r\n,'$'   lea bx,g_sz LEABELLOOP:   ;小写转大写   mov si,cx   sub byte ptr[bx+si-1], 20h   loop LEABELLOOP     ;输出字符串  g_szNEqu   lea dx,g_sz   mov ah,09H   int 21H EXIT:       ;带返回码的退出   mov ax,4C00H   int 21H   code ends

img

作业

1. 输入5行文本,统计每行文本的单词的个数,并输出。

;输入5行文本,统计每行文本的单词的个数,并输出。 ;这里是数据 data segment    ;变量不允许重名   g_w dw   0     ;记录循环次数 g_sz1 db 6 dup('$')     ;用于输出长度的字符串 g_sz db 255 dup('$')   ;用于输入的字符串 g_Number db "0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F" ; 数字数组 data ends ;这里是代码 code  segment   ;段开始 LEABEL1:  ;初始化字符串 ;将字符串重置  mov al,'$'  lea di,g_sz        mov cx,length g_sz    REP stosb  lea di,g_sz1        mov cx,length g_sz1    REP stosb jmp  LEABEL2 START:  ;标号   assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds   mov es,ax        ;不直接给是因为没有立即数到段寄存器   mov cx,5  ;循环次数 LEABELLOOP: mov g_w,cx  jmp  LEABEL1 LEABEL2:      lea bx,g_sz           ;获取字符串 g_sz 的首地址  mov word ptr [bx],size  g_sz  ;把字符串大小给  byte ptr [bx]  mov dx,bx                     ;把地址给 dx   ;输入字符串  mov ah,0aH   ; 将功能编号给ah  int 21H     ;调用21号中断  mov ax,0  mov al,byte ptr[g_sz+1]    mov si,ax  mov word ptr [g_sz+2+si],0d0aH   ;添加字符串后加'\r\n'  lea dx,g_sz           ;获取字符串 g_sz 的首地址  add dx,2  ;输出字符传串  mov ah,9   ; 将功能编号给ah  int 21H     ;调用21号中断   ;将长度给g_sz1   ;把DX清0     mov dx,0   mov dl,byte ptr[g_sz+1]     ;然后取出低四位去数组里找到对应的存储   mov cl,4   shr dx,cl;右移四位得到低四位   mov si,dx   mov cl,g_Number[si] ;把数组第bx个元素给cx寄存器   ;然后把cl里的值给输出数字的字符串   mov byte ptr [g_sz1] ,cl   ;开始取第二位   mov dx,0;   mov dl,byte ptr[bx+1]     mov cl,12   shl dx,cl   shr dx,cl   mov si,dx   mov cl,g_Number[si] ;把数组第bx个元素给cx寄存器   ;然后把cl里的值给输出数字的字符串   mov byte ptr [g_sz1+1] ,cl   mov word ptr [g_sz1+2],0d0aH   ;添加字符串后加'\r\n'   lea dx,g_sz1           ;获取字符串 g_sz 的首地址   ;输出字符传串   mov ah,9   ; 将功能编号给ah   int 21H     ;调用21号中断   mov cx,g_w   loop LEABELLOOP   EXIT:     mov ax,4C00H  ; 上面2条指令合成一条     int 21h   ;使用21号中断 code ends        ;段结束 end  START  ;代表从该标号第一行代码作为运行起点

2. 用汇编模仿 if-else,while(){},do-while。

if-else

;两个数比较.相等则输出eaqul,否则输出  not eaqul stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w0 dw 1122H g_w1 dw 1123H g_szEqu db "eaqul",0dh,0ah,'$' g_szNEqu db "not eaqul",0dh,0ah,'$' data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,g_w0   ;将 g_w0 的值 给 ax   cmp ax,g_w1   ; ax的值 和 g_w2 作比较   jz  LEABEL2   ; 如果比较的值相等则跳转 LEABEL2 标号处   jnz  LEABEL3  ; 如果比较的值不相等则跳转 LEABEL3 标号处 LEABEL4:     jmp EXIT LEABEL2:   ;输出字符串  g_szEqu   lea dx,g_szEqu   mov ah,09H   int 21H   jmp LEABEL4 LEABEL3:   ;输出字符串  g_szNEqu   lea dx,g_szNEqu   mov ah,09H   int 21H   jmp LEABEL4 EXIT:       ;带返回码的退出   mov ax,4C00H   int 21H   code ends   end START

while(){}

; 求  1+2+3+.....+100 stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w0 dw 100 data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,0   mov bx,0   mov cx,7FFF LEABELLOOP:   cmp bx,g_w0  ;bx是否小于100   jAE EXIT     ;如果bx大于等于 g_w0 跳出循环   add  ax,bx   ;把 ax 的值加上 bx   inc bx       ; bx 自增1   loop LEABELLOOP   EXIT:       ;带返回码的退出   mov ax,4C00H   int 21H   code ends   end START

do-while

stack segment stack    ;栈段 stack ends   data segment   ;数据段 g_w0 dw 101 data ends   code segment  ;代码段 START:     assume ds:data   ;指定data段 作为 ds 段   mov ax,data      ;先把data的偏移值给ax   mov ds,ax        ;ax再把data的偏移值给ds                    ;不直接给是因为没有立即数到段寄存器   mov ax,0   mov bx,0   mov cx,7FFF LEABELLOOP:     add  ax,bx   ;把 ax 的值加上 bx   inc bx       ; bx 自增1   cmp bx,g_w0  ;bx是否小于100   jAE EXIT     ;如果bx大于等于 g_w0 跳出循环   loop LEABELLOOP   EXIT:       ;带返回码的退出   mov ax,4C00H   int 21H   code ends   end START


最新回复 ( 0 )
全部楼主