大理寺少卿 发表于 2024-12-21 22:18:19

8086汇编(16位汇编)学习笔记06.串操作、流程转移指令

## 串操作

### 串操作

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

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

!(./notesimg/1652439126466-47b8425c-73a0-4544-8c76-d62c628c1746.png)

### 串方向

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

!(./notesimg/1652440709344-88255af0-335d-43f1-bef3-46b97e7de718.png)

### 重复前缀

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

!(./notesimg/1652440851978-e6b10150-4e89-4861-8bfd-0e20a938ac00.png)

## 流程转移指令

### 1.无条件跳转

#### 直接转移jmp

!(./notesimg/1652441978436-3d06516c-3a6a-40c5-a318-c06129f5a125.png)

##### 短跳   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
```

!(./notesimg/1652442790532-4b2b9f8b-e846-4cfa-942d-b91063edbddf.png)

!(./notesimg/1652442930896-95bc5ad9-951e-44a9-94b4-9dce2e1559e1.png)

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

#### !(./notesimg/1652443111550-4f8da599-b8ef-4bd8-a196-a87cd4ebbff1.png)

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
```

!(./notesimg/1652444195657-862498e1-3d1d-43d4-9e90-36e7f1ffe480.png)


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

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

##### 近跳    jmp或者jmpnear

短跳的偏移值 范围 是 - 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
```

!(./notesimg/1652444950943-25bc7fbf-0f9f-4adc-a111-609674372f51.png)

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

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

##### 远跳   jmp far

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

!(./notesimg/1652445718200-a997753d-bf91-4aa9-bc48-adde85712659.png)

```
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 段名:标号名,效果跟上面一样

    jmpLEABEL1; 跳转到标号 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
```

!(./notesimg/1652446128460-f8ef082e-d65f-4dc9-acc0-ff49e66e760e.png)

##### 指令偏移计算

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

!(./notesimg/1652446424441-204567e8-1884-402c-81ca-0ebdd9d47bff.png)

##### 指令偏移计算练习

计算下列指令的机器码,短跳 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

!(./notesimg/1652446627368-d7e87fd2-e5b2-4953-8745-94eb7d8fe610.png)

!(./notesimg/1652446922419-8b62930e-e0d4-4827-9441-a3ba2648db92.png)

!(./notesimg/1652447189228-b6d6ae27-93fa-4a3b-b805-c67e40291502.png)

!(./notesimg/1652447696862-3e80369e-b8ca-475c-ab12-f49e92d8910c.png)

!(./notesimg/1652448252972-765fd691-3e5b-4710-a613-a46e3bbb6385.png)

##### 使用寄存器间接转移

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

!(./notesimg/1652449360642-67536038-2189-48ca-8695-66be5d30a8ea.png)

##### 使用EA的间接转移

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

!(./notesimg/1652449400072-8ae3c750-e1a2-4e29-bf46-4dca5b56930c.png)

**注意:**

```
```

**在运用转移的时候需要遵循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, offsetLEABEL4 ;将 LEABEL4 的偏移值给 g_w
    jmp g_w
   
    mov ax,ax
    xor ax,ax

LEABEL4:   

    ;带返回码的退出
    mov ax,4C00H
    int 21
   
code ends
end START
```

!(./notesimg/1652450045696-d680c29f-eea9-45ec-94c9-a887fe8554a4.png)

!(./notesimg/1652450114193-aa9037b6-d035-412d-b26e-3136794316ec.png)

```
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
    movg_wCS,seg LEABEL3   ;将 LEABEL3 的段基址给 g_wCS

    lea bx,g_wEA             ;将 g_wEA 的偏移地址给 bx
    jmp dword ptr        ;取出偏移值为bx (即 g_wEA) 处的值,并跳转到该处

   
    jmp g_w
   
    mov ax,ax
    xor ax,ax

LEABEL4:   

    ;带返回码的退出
    mov ax,4C00H
    int 21
   
code ends
end START
```

!(./notesimg/1652450838211-16338d69-1b55-494a-8f0b-5453fa57d600.png)

#### 2.条件跳转

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

##### 单条件跳转

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

!(./notesimg/1652451103297-88ad2ecc-f668-44a4-9b57-772306b47925.png)

###### 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 作比较

    jzLEABEL2   ; 如果比较的值相等则跳转 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 作比较

    jzLEABEL2   ; 如果比较的值相等则跳转 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
```

!(./notesimg/1652452032466-48f6c45b-3ed3-4689-a2aa-596702d37d99.png)

!(./notesimg/1652453542302-bfc8b993-1e81-4905-9226-0364103c1559.png)

!(./notesimg/1652453272240-b87a553f-22a3-4876-b009-e7183e4ce937.png)

###### 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
```

!(./notesimg/1652454520315-ac8abc76-65c2-451b-b0e5-d005789920ca.png)

C:\>DEBUG JMP.EXE

-G

EAQUI

NOT

NORMALLY (0000)

PROGRAM TERMINATED

!(./notesimg/1652453888573-8d91c8ce-ae9a-49e2-8df3-06b6be17739c.png)

无符号数判断

!(./notesimg/1652451128442-1c544903-eeae-45bc-b7cf-608ccb506605.png)

!(./notesimg/1652454897349-88826a23-c440-4833-9c3c-5aa39261f159.png)

有符号数判断

!(./notesimg/1652451163797-4a0e03ca-84ba-4638-986e-6a914d63f6ef.png)

3.LOOP

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

格式: loop 标号

!(./notesimg/1652455048824-02742e41-8879-49b6-b723-c8061e1e03d4.png)

!(./notesimg/1652455048824-02742e41-8879-49b6-b723-c8061e1e03d4-170090686463533.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, 20h
    loop LEABELLOOP

    ;输出字符串g_szNEqu
    lea dx,g_sz
    mov ah,09H
    int 21H

EXIT:

    ;带返回码的退出
    mov ax,4C00H
    int 21H
   
code ends
```

!(./notesimg/1652455797287-94290a3b-9084-4533-a1ab-edb0ca177c2c.png)

## 作业

#### 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

;这里是代码
codesegment   ;段开始

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
jmpLEABEL2

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
   jmpLEABEL1

LEABEL2:   
   lea bx,g_sz         ;获取字符串 g_sz 的首地址
   mov word ptr ,sizeg_sz;把字符串大小给byte ptr
   mov dx,bx                     ;把地址给 dx
;输入字符串
   mov ah,0aH   ; 将功能编号给ah
   int 21H   ;调用21号中断

   mov ax,0
   mov al,byte ptr
   mov si,ax
   mov word ptr ,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
    ;然后取出低四位去数组里找到对应的存储
    mov cl,4
    shr dx,cl;右移四位得到低四位

    mov si,dx
    mov cl,g_Number ;把数组第bx个元素给cx寄存器
    ;然后把cl里的值给输出数字的字符串
    mov byte ptr ,cl

    ;开始取第二位
    mov dx,0;
    mov dl,byte ptr
    mov cl,12
    shl dx,cl
    shr dx,cl
    mov si,dx
    mov cl,g_Number ;把数组第bx个元素给cx寄存器

    ;然后把cl里的值给输出数字的字符串
    mov byte ptr ,cl
    mov word ptr ,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      ;段结束
endSTART;代表从该标号第一行代码作为运行起点
```

#### 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 作比较

    jzLEABEL2   ; 如果比较的值相等则跳转 LEABEL2 标号处
    jnzLEABEL3; 如果比较的值不相等则跳转 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 跳出循环
    addax,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:
    addax,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
```
页: [1]
查看完整版本: 8086汇编(16位汇编)学习笔记06.串操作、流程转移指令