当前位置: 首页 > news >正文

从入门到精通汇编语言 第五章(流程转移与子程序)

参考教程:通俗易懂的汇编语言(王爽老师的书)_哔哩哔哩_bilibili

一、“转移”概述

1、转移的概念

(1)般情况下指令是顺序地逐条执行的,而在实际中,常需要改变程序的执行流程,这就需要使用到转移指令。

(2)转移指令可以控制CPU执行内存中某处代码的指令,它通过修改IP寄存器,或同时修改CS寄存器和IP寄存器实现

2、转移的分类

(1)按转移行为分类:

①段内转移:只修改IP寄存器,如“JMP AX”。

②段间转移:同时修改CS寄存器和IP寄存器,如“JMP 1000:0”。

(2)根据指令对IP寄存器修改的范围(即在原内容基础上的修改幅度)不同分类(非全):

①段内短转移:IP修改范围为-128~127。

②段内近转移:IP修改范围为-32768~32767。

(3)按转移指令分类:

①无条件转移指令。

②条件转移指令。

③循环指令。

④中断。

⑤过程调用。

3、取得标号的偏移地址及段地址

(1)使用操作符offset可取得标号对应内存在其段中的偏移地址使用操作符seg可取得标号对应内存在其段的段地址

(2)举例:程序在运行中将s处的一条指令复制到s0处。

assume cs:codesg

codesg segment

        s:     mov ax, bx

                mov si, offset s  ;将标号s对应内存在其段中的偏移地址存入SI

                mov di, offset s0  ;将标号s0对应内存在其段中的偏移地址存入DI

                mov ax, cs:[si]   ;将s处的指令(占两个字节)内容送入AX

                mov cs:[di], ax  ;将AX中的内容送入s0对应的内存

        s0:   nop     ;空语句,占一个字节

                nop     ;空语句,占一个字节

codesg ends

end start

二、JMP指令

1、概述

(1)JMP指令可实现无条件转移,可以只修改IP寄存器,也可以同时修改CS寄存器和IP寄存器

(2)JMP指令要给出两种信息的其中一个:

①转移的距离(转移有向前和向后之分,转移距离分别对应正数和负数,用补码表示)。

②转移的目的地址。

2、依据位移进行转移

(1)依据位移进行转移可分为段内短转移和段内近转移。

①段内短转移:IP修改范围为-128~127,指令格式为“JMP short <标号>”。

②段内近转移:IP修改范围为-32768~32767,指令格式为“JMP near ptr <标号>”。

(2)jmp short的机器指令中,包含的是跳转到指令的相对位置,而不是转移的目标地址(简而言之,就是转移距离,范围为-128~127,超出范围会报错),如下所示。

assume cs:codesg

codesg segment

        start:  mov ax, 0

                  jmp short s   ;程序员写入标号即可,8位转移距离由编译器计算

                  add ax, 1

        s:       nop     ;空语句,占一个字节

codesg ends

end start

①当程序运行至“jmp short s”指令时,(IP) = 0003H,CS:IP指向EB 05(“jmp short s”指令的机器码)。

②CPU读取指令码EB 05进入指令缓冲器。

③(IP) = (IP) + 2 = 0005H(2为指令码EB 05的长度),此时CS:IP指向指令“add ax, 1”。

④CPU执行指令缓冲器中的指令EB 05。

⑤指令EB 05执行后,(IP) = (IP) + 05 = 000AH,CS:IP指向“nop”。

(3)jmp near ptr的机器指令中,包含的是跳转到指令的相对位置,而不是转移的目标地址(简而言之,就是转移距离,范围为-32768~32767,超出范围会报错),如下所示。

assume cs:codesg

codesg segment

        start:  mov ax, 0

                  jmp near ptr s   ;程序员写入标号即可,16位转移距离由编译器计算

                  db 128 dup (0)

        s:       nop     ;空语句,占一个字节

codesg ends

end start

①当程序运行至“jmp near ptr s”指令时,(IP) = 0003H,CS:IP指向E9 00 01(“jmp near ptr s”指令的机器码)。

②CPU读取指令码E9 00 01进入指令缓冲器。

③(IP) = (IP) + 3 = 0006H(3为指令码E9 00 01的长度),此时CS:IP指向128个字节数据的第一个字节。

④CPU执行指令缓冲器中的指令E9 00 01。

⑤指令E9 00 01执行后,(IP) = (IP) + 100H = 0106H,CS:IP指向“nop”。

3、依据目的地址进行转移

(1)远转移的指令格式为“JMP far ptr <标号>”,far ptr指明了跳转到的目的地址,即包含了标号的段地址CS和偏移地址IP

(2)举例:

assume cs:codesg

codesg segment

        start:  mov ax, 0

                  mov bx, 0

                  jmp far ptr s   ;程序员写入标号即可,目的地址由编译器计算

                  db 128 dup (0)

        s:       nop     ;空语句,占一个字节

codesg ends

end start

①当程序运行至“jmp far ptr s”指令时,(IP) = 0006H,CS:IP指向EA 0B 01 6A 07(“jmp far ptr s”指令的机器码)。

②CPU读取指令码EA 0B 01 6A 07进入指令缓冲器。

③(IP) = (IP) + 5 = 000BH(5为指令码E9 00 01的长度),此时CS:IP指向128个字节数据的第一个字节。

④CPU执行指令缓冲器中的指令EA 0B 01 6A 07。

⑤指令EA 0B 01 6A 07执行后,(IP) = 010B,(CS) = 076A。

4、转移地址在寄存器或内存中的JMP指令

(1)转移地址在寄存器中的JMP指令:

①格式:JMP <16位寄存器>。(如“jmp ax”)

②作用:寄存器中存放着一个字,它是转移的目的偏移地址

(2)转移地址在内存中的JMP指令:

①段内转移:

[1]格式:JMP word ptr <内存单元地址>或JMP word ptr <段寄存器>:<内存单元地址>。

[2]作用:从内存单元地址处开始存放着一个字,它是转移的目的偏移地址

[3]举例:

②段间转移:

[1]格式:JMP dword ptr <内存单元地址>或JMP dword ptr <段寄存器>:<内存单元地址>。

[2]作用:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址

[3]举例:

三、一些其它的转移指令

1、JCXZ指令

(1)指令格式:JCXZ <标号>。

(2)指令功能:如果(cx)=0,执行此条指令后将程序转移至标号处,否则执行此条指令后程序按顺序继续执行指令

(3)此条指令改变的是IP寄存器中的值,修改范围为-128~127,底层原理同“JMP short <标号>”,此处不再赘述

2、LOOP指令

(1)指令格式:LOOP <标号>。

(2)指令功能:如果(cx)=0,执行此条指令后程序按顺序继续执行指令,否则执行此条指令后将程序转移至标号处

(3)此条指令改变的是IP寄存器中的值,修改范围为-128~127,底层原理同“JMP short <标号>”,此处不再赘述

四、CALL指令和RET指令

1、CALL指令

(1)根据字面意思,CALL指令用于调用子程序,其本质是流程转移,底层原理和JMP指令类似。

(2)指令格式:CALL <标号> 或CALL far ptr <标号> 或CALL <寄存器>。

①CPU执行“CALL <标号> ”指令时,会进行两步操作:

[1]将当前的IP压入栈中,即(sp) = (sp) - 2、((ss) * 16 + (sp)) = (IP)。

[2]转移到标号处执行指令,即(IP) = (IP) + 16位位移(16位位移 = <标号>处的地址 - CALL指令后的第一个字节的地址,范围为-32768~32767,用补码表示,它由编译程序在编译时算出)。

②CPU执行“CALL far ptr <标号> ”指令时,会进行两步操作:

[1]将当前的CS和IP依次压入栈中,即(sp) = (sp) - 2、((ss) * 16 + (sp)) = (CS)、(sp) = (sp) - 2、((ss) * 16 + (sp)) = (IP)。

[2]转移到标号处执行指令,(CS) = 标号所在的段地址、(IP) = 标号所在的偏移地址。

③CPU执行“CALL <16位寄存器> ”指令时,会进行两步操作:

[1]将当前的IP压入栈中,即(sp) = (sp) - 2、((ss) * 16 + (sp)) = (IP)。

[2]将IP寄存器中的值置为<16位寄存器>中的值

(3)除了上面三种指令格式以外,转移地址还可通过访问特定内存地址给出。

①CALL word ptr <内存单元地址>:<内存单元地址>指向一个字数据,该指令会将IP寄存器中的值改为<内存单元地址>中存放的一个字数据。

②CALL dword ptr <内存单元地址>:<内存单元地址>指向两个字数据,该指令会将IP寄存器中的值改为<内存单元地址>指向内存中低地址存放的字数据,将CS寄存器中的值改为<内存单元地址>指向内存中高地址存放的字数据。

2、RET指令和RETF指令

(1)RET指令可以没有操作数,它相当于指令“POP IP”,用栈中的数据修改IP的内容,从而实现近转移;RETF指令同样也可以没有操作数,它相当于指令“POP IP”与“POP CS”,用栈中的数据修改IP和CS的内容,从而实现远转移

(2)RET指令和RETF指令通常配合CALL指令使用,从而实现子程序的调用,后续主要使用RET指令进行介绍。

五、MUL指令

1、MUL指令的功能和用法

(1)MUL是乘法指令,使用MUL做乘法的时候,被乘数默认放在AL或AX中,乘数放在其它寄存器或内存单元中,积存放在AX或AX、DX中,具体如何放置见下表

被乘数的长度为8位

被乘数的长度为16位

被乘数存放的位置

AL

AX

乘数存放的位置

8位长度的内存单元或寄存器

16位长度的内存单元或寄存器

积存放的位置

AX

DX(积高16位)和AX(积低16位)

(2)MUL指令格式:MUL <寄存器或内存单元地址>。

(3)使用MUL指令时,切记提前在默认的寄存器中设置好被乘数,且默认寄存器不作别的用处。

2、MUL指令使用举例

(1)计算10×100:

assume cs:code

code segment

        main: mov al, 100   ;被乘数(8位)

                  mov bl, 10   ;乘数(8位)

                  mul bl

                  mov ax, 4c00h

                  int 21h

code ends

end main

(2)计算100×10000:

assume cs:code

code segment

        main: mov ax, 100   ;被乘数(16位)

                  mov bx, 1000   ;乘数(16位)

                  mul bl

                  mov ax, 4c00h

                  int 21h

code ends

end main

六、汇编语言的模块化程序设计

1、CALL指令和RET指令的配合使用

(1)CALL指令和RET指令配合使用,可以实现具有子程序的源程序,其中子程序可根据提供的参数处理一定的事务,处理后将结果(返回值)提供给调用者。

(2)具有子程序的源程序的框架:

assume cs:code, ss:stack

stack segment

                  db 16 dup (0)   ;为call和ret指令设置栈

stack ends

code segment

        main: ...

                  call sub1    ;调用子程序sub1

                  ...

                  mov ax, 4c00h

                  int 21h

        sub1: ...     ;子程序sub1开始

                  call sub2    ;调用子程序sub2

                  ...

                  ret     ;子程序返回

        sub2: ...     ;子程序sub2开始

                  ...

                  ret     ;子程序返回

code ends

end main

(3)举例:将AX中的值乘以2(AX的初始值为1000D),结果存放在AX寄存器中。

assume cs:code, ss:stack

stack segment

                  db 16 dup (0)   ;为call和ret指令设置栈

stack ends

code segment

        main: mov ax, stack

                  mov ss, ax   ;栈段地址初始化

                  mov sp, 16   ;栈顶指针初始化

                  mov ax, 1000   ;AX初始化

                  call s    ;调用子程序

                  mov ax, 4c00h

                  int 21h

        s:       add ax, ax   ;AX中的值乘以2,结果存放在AX中

                  ret     ;子程序返回

code ends

end main

2、参数和结果传递的问题

(1)例1:根据提供的N,计算N的3次方,要求将计算过程封装为一个子程序(或者说子函数)。

①解决方案——用寄存器与子函数完成参数和结果的传递:

[1]函数参数放到BX中,即(bx) = N。

[2]子程序中用多个MUL指令计算N^3。

[3]将结果放到DX和AX中。

②汇编程序:

assume cs:code, ds:data

data segment

                  dw 1,2,3,4,5,6,7,8  ;提供的N

                  dd 0,0,0,0,0,0,0,0  ;N的3次方计算结果保存在此处

data ends

code segment

        main: mov ax, data

                  mov ds, ax   ;数据段地址初始化

                  mov si, 0    ;DS:SI指向数据段第一个字

                  mov di, 16   ;DS:DI指向数据段保存计算结果区域的第一个双字

                  mov cx, 8

        s:       mov bx, [si]   ;将N通过BX传递给子程序

                  call cube    ;调用子程序进行计算

                  mov [di], ax   ;积的低16位存到计算结果区域的低地址字

                  mov [di].2, dx   ;积的高16位存到计算结果区域的高地址字

                  add si, 2    ;DS:SI指向数据段下一个字

                  add di, 4    ;DS:DI指向数据段保存计算结果区域的下一个双字

                  loop s

                  mov ax, 4c00h

                  int 21h

        cube: mov ax, bx

                  mul bx

                  mul bx

                  ret     ;子程序返回

code ends

end main

③缺陷:计算结果可能大于32位,甚至更大,如果全部借助寄存器进行传递,显然是不现实的。

(2)例2:将data段中的字符串转化为大写。

①解决方案——用内存单元与子函数完成参数和结果的传递

[1]将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序

[2]对于具有批量数据的返回结果,也可用同样的方法。

②汇编程序:

assume cs:code, ds:data

data segment

                  db ‘conversation’

data ends

code segment

        main: mov ax, data

                  mov ds, ax    ;数据段地址初始化

                  mov si, 0     ;DS:SI指向数据段第一个字

                  mov cx, 12    ;总共需要转换12个字母

                  call capital    ;调用子程序进行大写字母转换

                  mov ax, 4c00h

                  int 21h

        capital: and byte ptr [si], 11011111b ;将DS:SI指向的字母转换为大写

                  inc si     ;(si) = (si) + 1

                  loop capital

                  ret      ;子程序返回

code ends

end main

(2)例3:计算(a-b)的3次方,假设a = 3、b = 1。

①解决方案——用栈与子函数完成参数的传递:由调用者将需要传递给子程序的参数压入栈中,子程序从栈中取得参数

②知识补充:“RET idata”指令的含义为“pop ip”、“add sp, n”,栈顶指针sp下移后,虽然栈中的数据没有被删除,但它们在逻辑上相当于被删除了,不会影响栈的正常运作;“RETF idata”同理,这里不再赘述

③汇编程序:

assume cs:code

code segment

        main: mov ax, 1

                  push ax     ;参数b压入栈中

                  mov ax, 3

                  push ax     ;参数a压入栈中

                  call difcube    ;调用子程序进行算式计算

                  mov ax, 4c00h

                  int 21h

        difcube: push bp  ;要借用BP寄存器,首先需要保护其原本的内容,将其入栈

                  mov bp, sp ;获取栈顶指针,存在BP寄存器中

                  mov ax, [bp+4] ;获取参数a,将其存入AX中

                  sub ax, [bp+6]  ;获取参数b,计算a-b,将其存入AX中

                  mov bp, ax

                  mul bp

                  mul bp

                  pop bp  ;BP寄存器借用完成,将其原本的内容从栈顶还原回去

                  ret 4   ;子程序返回,并逻辑删除形式参数a和b

code ends

end main

④程序的执行过程中栈的变化:

七、寄存器冲突问题

1、可能引起冲突的情况

(1)在编写子程序时,子程序使用了主程序正在使用的寄存器,且寄存器中有主程序后续需要使用到的数据,这种情况下子程序很可能会影响主程序的正常运作。

(2)举例:将data段中的字符串转化为大写。

assume cs:code, ds:data

data segment

                  db ‘word’, 0    ;’\0’是字符串的结束标志

                  db ‘byte’, 0    ;’\0’是字符串的结束标志

                  db ‘uint’, 0    ;’\0’是字符串的结束标志

                  db ‘char’, 0    ;’\0’是字符串的结束标志

data ends

code segment

        main: mov ax, data

                  mov ds, ax    ;数据段地址初始化

                  mov bx, 0

                  mov cx, 4     ;需要转换4个单词

        s:       mov si, bx    ;DS:SI指向字符串首地址

                  call capital    ;调用子程序对一个单词进行大写字母转换

                  add bx, 5     ;使DS:SI指向下一个字符串首地址

                  loop s

                  mov ax, 4c00h

                  int 21h

        capital: mov cl, [si]    ;主程序中的循环结构正在用CX寄存器

                  mov ch, 0     ;子程序直接占用CX寄存器显然是有问题的

                  jcxz ok     ;判断DS:SI指向的是否是’\0’,是则结束

                  and byte ptr [si], 11011111b ;将DS:SI指向的字母转换为大写

                  inc si     ;DS:SI指向下一个字母

                  jmp short capital

        ok:     ret      ;子程序返回

code ends

end main

2、冲突问题的解决

(1)冲突问题有两种解决方案,如下所示,最为常用的是第二种,使用第二种方案时,编写调用了程序的程序的时候不必关心子程序到底使用了哪些寄存器,编写子程序的时候也不必关心调用者使用了哪些寄存器。

①在编写调用子程序的程序时,程序员需要注意子程序中有没有用到会产生冲突的寄存器,如果有,使用别的寄存器,不要使用会产生冲突的寄存器。

在子程序的开始将要用到的所有寄存器中的内容都保存起来,在子程序返回前再恢复

(2)子程序标准框架:

子程序开始: 子程序中使用的寄存器入栈

                    子程序内容

                    子程序使用的寄存器出栈

                    返回(ret、retf)

(3)寄存器冲突问题的解决示例:将data段中的字符串转化为大写。

assume cs:code, ds:data

data segment

                  db ‘word’, 0    ;’\0’是字符串的结束标志

                  db ‘byte’, 0    ;’\0’是字符串的结束标志

                  db ‘uint’, 0    ;’\0’是字符串的结束标志

                  db ‘char’, 0    ;’\0’是字符串的结束标志

data ends

code segment

        main: mov ax, data

mov ds, ax    ;数据段地址初始化

mov bx, 0

mov cx, 4     ;需要转换4个单词

s:  mov si, bx    ;DS:SI指向字符串首地址

call capital    ;调用子程序对一个单词进行大写字母转换

add bx, 5     ;使DS:SI指向下一个字符串首地址

loop s

mov ax, 4c00h

int 21h

capital: push cx

push si

change: mov cl, [si]    ;将DS:SI指向的字符送入CL

mov ch, 0     ;CH置0

jcxz ok     ;判断DS:SI指向的是否是’\0’,是则结束

and byte ptr [si], 11011111b ;将DS:SI指向的字母转换为大写

inc si     ;DS:SI指向下一个字母

jmp short capital

ok:  pop si

pop cx

ret      ;子程序返回

code ends

end main

八、标志寄存器

1、标志寄存器介绍

(1)标志寄存器的简写为PSW或FLAGS,又称程序状态字。

(2)标志寄存器的结构:

①FLAGS寄存器是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息

②8086CPU中没有使用FLAGS的1、3、5、12、13、14、15位,这些位不具有任何含义。

(3)标志寄存器的作用:

用来存储相关指令的某些执行结果

用来为CPU执行相关指令提供行为依据

用来控制CPU的相关工作方式

(4)直接访问标志寄存器的方法:

①pushf指令:将标志寄存器的值压栈。

②popf指令:从栈中弹出数据,送入标志寄存器中。

(5)在8086CPU的指令集中,有的指令的执行是影响标志寄存器的,比如add、sub、mul、div、inc、or、and等,它们大都是运算指令,进行逻辑或算术运算;有的指令的执行对标志寄存器没有影响,比如mov、push、pop等,它们大都是传送指令。

(6)使用一条指令的时候,要注意这条指令的全部功能,其中包括执行结果对标记寄存器的哪些标志位造成影响。

2、ZF-零标志

(1)ZF标记相关指令的计算结果是否为0

①ZF=1,表示“结果是0”,1表示“逻辑真”

②ZF=0,表示“结果不是0”,0表示“逻辑假”。

(2)举例:

;and指令一共两个操作数,它对两个操作数执行按位与操作,结果存在操作数1中

mov ax, 1

and ax, 0   ;ZF = 1,表示“结果是0”

;or指令一共两个操作数,它对两个操作数执行按位或操作,结果存在操作数1中

mov ax, 1

or ax, 0   ;ZF = 0,表示“结果不是0”

3、PF-奇偶标志

(1)一条指令执行后,结果的所有二进制位中,若1的个数为偶数,PF = 1,否则若1的个数为奇数,PF = 0

(2)举例:

mov al, 1

add al, 10   ;计算结果为0000 1011B,其中有3个1,PF = 0

mov al, 1

or al, 2   ;计算结果为0000 0011B,其中有2个1,PF = 1

4、SF-符号标志

(1)一条指令执行后,将结果视为有符号数,若结果为负,SF = 1,否则若结果为非负,SF = 0

(2)举例:

mov al, 10000001b

add al, 1   ;计算结果为1000 0010B,为负数,SF = 1

sub ax, ax  ;计算结果为0000 0000 0000 0000B,为正数,SF = 0

(3)SF标志是CPU对有符号数运算结果的一种记录,将数据当作有符号数来运算的时候,通过SF可知结果的正负,将数据当作无符号数来运算,SF的值则没有意义,虽然相关的指令影响了它的值。

5、CF-进位标志

(1)对于位数为N的无符号数来说,其对应的二进制信息的最高位即第N-1位,是最高有效位,假想存在的第N位,就是相对最高有效位的更高位。

(2)在进行无符号数运算的时候,CF记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值一条指令执行后,若过程中有进位或借位,CF=1,否则若过程中无进位或借位,CF=0

(3)举例:

mov al, 98h

add al, al   ;计算过程产生进位,(al) = 30H,CF = 1

add al, al   ;计算过程未产生进位或借位,(al) = 60H,CF = 0

sub al, 98h  ;计算过程产生借位,(al) = C8H,CF = 1

6、OF-溢出标志

(1)在进行有符号数运算的时候,如结果超过了机器所能表示的范围称为溢出有符号数操作指令执行后,若有溢出,OF = 1,否则若无溢出,OF = 0

(2)举例:

mov al, 98

add al, 99   ;98 + 99 = 197,超出了8位有符号数的表示范围,OF = 1

mov al, 0f0h

add al, 88h  ;(-16) + (-120) = -136,超出了8位有符号数的表示范围,OF = 1

九、带进(借)位的加(减)法

1、ADC指令

(1)ADC是带进位加法指令,它利用了CF位上记录的进位值,其功能与ADD指令类似,不同的是,它在将两个操作数相加的同时还会加上CF标志的值

(2)指令格式:ADC <操作数1>, <操作数2>。

(3)举例:

①目的:编写一个子程序,对两个128位数据(低地址存放数的低位)进行相加,运算结果存储在第一个数的存储空间中。

②汇编程序:

assume cs:code, ds:data

data segment

                  dw 0A452H,0A8F5H,78E6H,0A8EH,8B7AH,54F6H,0F04H,671EH

                  dw 0E71EH,0EF04H,54F6H,8B7AH,0A8EH,78E6H,58F5H,0452H

data ends

code segment

        main: mov ax, data

                  mov ds, ax    ;数据段地址初始化

                  mov si, 0     ;DS:SI指向第一个操作数首地址

                  mov di, 16    ;DS:DI指向第二个操作数首地址

                  mov cx, 8     ;需要进行8次加法

                  call add128    ;调用子程序进行加法

                  mov ax, 4c00h

                  int 21h

        add128: push ax

                  push cx

                  push si

                  push di

                  sub ax, ax    ;进位标志清零

        s:       mov ax, [si]

                  adc ax, [di]    ;带进位加法

                  mov [si], ax    ;将结果保存在第一个数的存储空间中

                  inc si     ;DS:SI指向操作数高位

                  inc si     ;此处不可用add指令,会影响CF

                  inc di     ;DS:SI指向操作数高位

                  inc di     ;此处不可用add指令,会影响CF

                  loop s

                  pop di

                  pop si

                  pop cx

                  pop ax

                  ret      ;子程序返回

code ends

end main

2、SBB指令

(1)SBB是带借位减法指令,它利用了CF位上记录的减位值,其功能与SUB指令类似,不同的是,它在将两个操作数相减的同时还会减去CF标志的值

(2)指令格式:SBB <操作数1>, <操作数2>。

(3)举例:

①目的:计算003E1000H-00202000H,结果放在AX和BX中。

②汇编程序:

assume cs:code

code segment

        main: mov bx, 1000h   ;被减数低16位存入BX

                  mov ax, 003eh    ;被减数高16位存入AX

                  sub bx, 2000h    ;低16位先做减法

                  sbb ax, 0020h    ;高16位再做减法,并带上借位

                  mov ax, 4c00h

                  int 21h

code ends

end main

十、CMP指令与条件转移指令

1、CMP指令

(1)指令格式:CMP <操作数1>, <操作数2>。

(2)CMP指令会计算操作数1减去操作数2的结果,但它并不保存结果,也不改变两个操作数,仅仅影响标志寄存器,其它相关指令(通常是条件转移指令)通过识别这些被影响的标志寄存器位可以得知两个操作数的比较结果

(3)无符号数比较与标志位取值(以指令“CMP AX, BX”为例):

(4)有符号数比较与标志位取值(以指令“CMP AX, BX”为例):

2、条件转移指令

(1)几个英文字母的含义:

(2)根据单个标志位转移的指令:

(3)根据无符号数比较结果进行转移的指令:

(4)根据有符号数比较结果进行转移的指令:

3、CMP指令与条件转移指令的配合使用举例

(1)目标:子程序If_else实现逻辑“如果(ah)=(bh),则(ah)=(ah)+(ah),否则(ah)=(ah)+(bh)”。

(2)子程序实现:

If_else: cmp ah, bh ;进行两数比较

                je s   ;如果两数相等,跳转至标号s,否则顺序执行

                add ah, bh ;不满足条件执行的语句

                jmp short ok ;跳转至后面,避免同时执行了两个分支的操作

         s:    add ah, ah ;满足条件执行的语句

         ok:  ret

十一、串传送

1、DF-方向标志

(1)该标志位用来控制CPU的相关工作方式,具体表现如下:

①DF = 0:每次操作后SI寄存器、DI寄存器后,SI寄存器、DI寄存器的值自增。

②DF = 1:每次操作后SI寄存器、DI寄存器后,SI寄存器、DI寄存器的值自减。

(2)对DF位进行设置的指令:

①CLD指令:将标志寄存器的DF位设为0(clear)。

②STD指令:将标志寄存器的DF位设为1(setup)。

2、串传送指令

(1)在前面举过将字符串复制到另一存储空间中的例子,当时的解决方法是程序员人为地通过寄存器将字符一位一位地进行传送,实际上要想实现这种串传送的功能,还可以借助串传送指令。

(2)执行一次以字节为单位传送的串传送指令——MOVSB会发生的事情:

①((es) * 16 + (di)) = ((ds) * 16 + (si))。

②若DF = 0,则(si) = (si) + 1、(di) = (di) + 1;若DF = 1,则(si) = (si) - 1、(di) = (di) - 1。

(3)执行一次以字为单位传送的串传送指令——MOVSW会发生的事情:

①((es) * 16 + (di)) = ((ds) * 16 + (si))。

②若DF = 0,则(si) = (si) + 2、(di) = (di) + 2;若DF = 1,则(si) = (si) - 2、(di) = (di) - 2。

3、REP指令

(1)REP指令常与串传送指令搭配使用。

(2)REP指令无操作数,它会根据CX中的值,重复执行它后面的指令(也能认为这个指令是REP指令的操作数)。

4、串传送应用举例

(1)目的:用串传送指令,将F000H段中的最后16个字符复制到data段中。

(2)汇编程序:

assume cs:code, ds:data

data segment

                  db 16 dup (0)

data ends

code segment

        main: mov ax, 0f000h

                  mov ds, ax

                  mov si, 0ffffh    ;F000:FFFFH是F000H段的最后一字节地址

                  mov ax, data    ;获取数据段地址

                  mov es, ax    ;数据段地址送入ES中

                  mov di, 15    ;ES:DI指向data段最后一字节

                  mov cx, 16    ;需要进行16次串传送

                  std      ;将DF位设为1,操作(si)和(di)时它们会递减

                  rep movsb    ;进行16次串传送

                  mov ax, 4c00h

                  int 21h

code ends

end main

相关文章:

从入门到精通汇编语言 第五章(流程转移与子程序)

参考教程&#xff1a;通俗易懂的汇编语言&#xff08;王爽老师的书&#xff09;_哔哩哔哩_bilibili 一、“转移”概述 1、转移的概念 &#xff08;1&#xff09;般情况下指令是顺序地逐条执行的&#xff0c;而在实际中&#xff0c;常需要改变程序的执行流程&#xff0c;这就…...

Redis下载

目录 安装包 1、使用.msi方式安装 2.使用zip方式安装【推荐方式】 添加环境变量 配置后台运行 启动&#xff1a; 1.startup.cmd的文件 2.cmd窗口运行 3.linux源码安装 &#xff08;1&#xff09;准备安装环境 &#xff08;2&#xff09;上传安装文件 &#xff08;3&…...

硬件工程师笔记——电子器件汇总大全

目录 1、电阻 工作原理 欧姆定律 电阻的物理本质 一、限制电流 二、分压作用 三、消耗电能&#xff08;将电能转化为热能&#xff09; 2、压敏电阻 伏安特性 1. 过压保护 2. 电压调节 3. 浪涌吸收 4. 消噪与消火花 5. 高频应用 3、电容 工作原理 &#xff08;…...

第一章,HCIA复习

抽象语言---->电信号抽象语言---编码 编码------二进制 二进制----电信号 OSI参考模型 TCP/IP模型&#xff08;4参考5对等&#xff09; 应用层&#xff1a;程序的编译过程&#xff1b;人机交互的接口。 表示层&#xff1a;数据格式化--->二进制 会话层&#xff1a;维护网…...

在 Debian 12 中恢复被删除的 smb.conf 配置文件

https://forum.ubuntu.com.cn/viewtopic.php?t494763 本文结合ai输出&#xff0c;内容中可能有些错误&#xff0c;但确实解决了我的问题&#xff0c;我采取保留完整输出的方式摘录。 在 Debian 12 中恢复被删除的 smb.conf 配置文件&#xff0c;需结合 dpkg 和 ucf&#xff08…...

Java开发软件

Main.java // 主类&#xff0c;用于测试学生管理系统 public class Main { public static void main(String[] args) { StudentManagementSystem sms new StudentManagementSystem(); // 添加学生 sms.addStudent(new Student(1, "Alice", 20)…...

SSRF学习

靶场 fofa搜&#xff1a;“重庆橙子科技”&#xff0c;里面找SSRF。 SSRF基础知识 绕过127限制 要查看127.0.0.1/flag.php&#xff0c;但是127被过滤。 绕过方法&#xff1a;使用不同的进制表示127.0.0.1即可。 二进制&#xff1a;01111111.00000000.00000000.00000001 八…...

使用virtualbox的HostOnly建立共享网络-实现虚拟机上网

目录 环境描述解决方案具体步骤1.新建一个virtual host-only ethernet adapter2.设置windows的wifi信号网络共享3.确认winows宿主网络信息3.1.wifi适配器的信息3.2.虚拟网卡的信息3.3.确认virtualbox中虚拟网卡的ip地址 4.虚拟机网卡设置5.虚拟机网络设置5.1.本地连接设置5.2.u…...

RNN的理解

对于RNN的理解 import torch import torch.nn as nn import torch.nn.functional as F# 手动实现一个简单的RNN class RNN(nn.Module):def __init__(self, input_size, hidden_size, output_size):super(RNN, self).__init__()# 定义权重矩阵和偏置项self.hidden_size hidden…...

Transformers是一种基于自注意力机制的神经网络模型

概述与发展历程 背景介绍 Transformers是一种基于自注意力机制的神经网络模型&#xff0c;最早由Google团队在2017年的论文《Attention Is All You Need》中提出。该模型旨在解决传统循环神经网络&#xff08;RNNs&#xff09;在处理长距离依赖关系时的低效性问题&#xff0c…...

leetcode0078. 子集-medium

1 题目&#xff1a;子集 官方标定难度&#xff1a;中 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#xff1a; 输入&#xff1…...

C++编程 希尔排序

步骤&#xff1a; 1.先选定一个小于N的整数gap作为第一增量&#xff0c;然后将所有距离为gap的元素分在同一组&#xff0c;并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量&#xff0c;重复上述操作… 2.当增量的大小减到1时&#xff0c;就相当…...

网络操作系统与应用服务器

1.通过PTR实现IP地址到主机域名的映射 2.在windows中,可以使用事件查看器来游览日志文件 3.IMAP即交互式邮件存取协议,邮件客户端可以使用其同步服务器和客户端之间的邮件列表 4.DHCP Discover ->DHCP Offer->DHCP Request->DHCP Ack 5.在DNS的资源记录中,类型A表…...

不确定与非单调推理的模糊推理

模糊推理是利用模糊性知识进行的一种不确定性推理。 模糊推理与前面讨论的不确定性推理的概率方法、可信度方法、D-S理论有着实质性的区别。前面那几种不确定性推理的理论基础是概率论,它所研究的事件本身有明确而确定的含义,只是由于发生的条件不充分,使得在条件与事件之间…...

Vite打包原理: Tree-shaking在Vue3项目中的实际效果

Vite打包原理: Tree-shaking在Vue3项目中的实际效果 随着前端开发技术的不断进步&#xff0c;Vue框架在国内外都备受青睐。而在Vue3项目中&#xff0c;Vite作为一款新型的构建工具&#xff0c;其支持的Tree-shaking技术成为了开发者关注的焦点之一。那么&#xff0c;Vite中Tree…...

LangChain4j语言模型选型指南:主流模型能力全景对比

LangChain4j语言模型选型指南&#xff1a;主流模型能力全景对比 前言 在大语言模型应用开发中&#xff0c;选择合适的底层模型提供商是架构设计的关键决策。LangChain4j作为Java生态的重要AI框架&#xff0c;其支持的20模型提供商各有独特的优势场景。本文通过功能矩阵深度解…...

聚宽策略----国九条后中小板微盘小改,年化135.40%

最近在研究的聚宽策略&#xff0c;一般技术分析的我直接转qmt了&#xff0c;财务因子有一点麻烦&#xff0c;我直接利用我开发强大的服务器系统&#xff0c;直接读取信号&#xff0c;最近在优化一下系统&#xff0c;最近在开发对接bigquant的交易系统&#xff0c;完成了api数据…...

FreeRTOS中断管理

中断优先级 任何中断的优先级都大于任务&#xff01; 在我们的操作系统&#xff0c;中断同样是具有优先级的&#xff0c;并且我们也可以设置它的优先级&#xff0c;但是他的优先级并不是从 0 ~ 5 &#xff0c;默认情况下它是从 5 ~ 15 , 0 ~ 4 这5个中断优先级不是FreeRTOS控…...

键入网址到网页显示,期间发生了什么?

文章目录 2.键入网址到网页显示&#xff0c;期间发生了什么&#xff1f;2.1真实地址查询DNS&#xff1a;2.2**协议栈&#xff1a;**上半部分是负责收发数据的TCP和UDP协议&#xff0c;下面一半是用IP协议控制网络包收发操作&#xff0c;在互联网上传数据时&#xff0c;数据会倍…...

代理模式(Proxy Pattern)

文章目录 1. 概述1.1 基本概念1.2 为什么需要代理模式1.3 代理模式的四个角色2. 代理模式的类型2.1 静态代理2.2 JDK动态代理2.3 CGLIB动态代理3. 代理模式的UML类图和基本实现3.1 UML类图3.2 基本实现3.2.1 静态代理基本实现3.2.2 JDK动态代理基本实现3.2.3 CGLIB动态代理基本…...

9.QT-显示类控件|Label|显示不同格式的文本|显示图片|文本对齐|自动换行|缩进|边距|设置伙伴(C++)

Label QLabel 可以⽤来显⽰⽂本和图⽚ 属性说明textQLabel中的⽂本textFormat⽂本的格式.• Qt::PlainText 纯⽂本• Qt::RichText 富⽂本(⽀持html标签)• Qt::MarkdownText markdown格式• Qt::AutoText 根据⽂本内容⾃动决定⽂本格式pixmapQLabel 内部包含的图⽚.scaledCo…...

Python多任务编程:进程全面详解与实战指南

1. 进程基础概念 1.1 什么是进程&#xff1f; 进程(Process)是指正在执行的程序&#xff0c;是程序执行过程中的一次指令、数据集等的集合。简单来说&#xff0c;进程就是程序的一次执行过程&#xff0c;它是一个动态的概念。 想象你打开电脑上的音乐播放器听歌&#xff0c;…...

【英语语法】词法---副词

目录 副词1. 副词的核心功能2. 副词的分类(1) 按意义分类(2) 按形式分类 3. 副词的构成(1) 形容词变副词的规则(2) 不规则变化 4. 副词的位置(1) 修饰动词时的位置(2) 多个副词的排列顺序 5. 副词的比较级与最高级(1) 规则变化&#xff08;同形容词&#xff09;(2) 不规则变化(…...

51c大模型~合集119

我自己的原文哦~ https://blog.51cto.com/whaosoft/13852062 #264页智能体综述 MetaGPT等20家顶尖机构、47位学者参与 近期&#xff0c;大模型智能体&#xff08;Agent&#xff09;的相关话题爆火 —— 不论是 Anthropic 抢先 MCP 范式的快速普及&#xff0c;还是 OpenAI…...

Day3:个人中心页面布局前端项目uniapp壁纸实战

接下来我们来弄一下个人中心页面布局user.vue <template><view class"userLayout"><view class"userInfo"><view class"avatar"><image src"../../static/Kx.jpg" mode"aspectFill"></im…...

多元协同网络拓扑模型

一、组织逻辑架构解构 1.1 多元协同网络拓扑模型 &#xff08;1&#xff09;产业链价值链重构图谱 阶段核心节点技术耦合系数商业转化周期基础层云服务供应商&#xff08;阿里云/ECS集群&#xff09;0.8512-24个月中台层开发者生态&#xff08;百炼平台/API网关&#xff09;…...

基于 Elasticsearch 8.12.0 集群创建索引

索引创建 创建一个产品的索引 解释&#xff1a; productId: 产品的唯一标识符&#xff0c;使用 keyword 类型&#xff0c;适合精确匹配和聚合操作。name: 产品名称&#xff0c;使用 text 类型进行全文搜索&#xff0c;同时包含一个 keyword 子字段用于精确匹配。description:…...

LeetCode283.移动零

给定一个数组 arr&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2 输入: nums [0] 输出: [0…...

select、poll、epoll实现多路复用IO并对比差异

目录 一、select实现多路复用 1.select函数介绍 2.select优缺点 3.select使用示例 二、poll实现多路复用 1.poll函数介绍 2.poll优缺点 3.poll使用示例 三、epoll实现多路复用 1.epoll函数介绍 2.epoll工作原理 3.epoll工作模式 (1)水平触发LT模式 (2)边缘触发ET模…...

FastAPI-MCP

介绍 开源地址&#xff1a; https://github.com/tadata-org/fastapi_mcp FastAPI-MCP 是一个开源项目&#xff0c;旨在简化 FastAPI 应用与现代 AI 代理&#xff08;如基于大语言模型的系统&#xff09;之间的集成。它通过自动将 FastAPI 的所有 API 端点暴露为符合 Model Co…...

Matlab 复合模糊PID

1、内容简介 Matlab 209-复合模糊PID 可以交流、咨询、答疑 2、内容说明 略摘 要:在并联型模糊 PID 复合控制器设计中,必须根据偏差大小及时地调整模糊控制部分和 PID 控制 部分的比例,而这种较为复杂的控制策略利用普通的 Simulink 模块是很难实现的.采用S-函数来解决 这个问…...

javaSE.队列

链表&#xff1a;屁股入队&#xff0c;头部出队 链尾入队&#x1f447; 是while(tail.next!null) &#x1f447; 链首出队&#xff08;head.next)&#x1f447; 仅获取队首&#x1f447;...

c++基础·左值右值

一、左值与右值的本质特征 1. 基础定义 左值 (lvalue) ✅ 可出现在赋值运算符左侧 ✅ 可被取地址&#xff08;有明确存储位置&#xff09; ✅ 通常为具名变量&#xff08;如int a 10;中的a&#xff09; 右值 (rvalue) ❌ 不可出现在赋值左侧 ❌ 不可取地址&#xff08;无持久…...

From RAG to Memory: Non-Parametric Continual Learning for Large Language Models

从RAG到记忆:大语言模型的无参数持续学习 原文链接:https://arxiv.org/pdf/2502.14802 Code: https://github.com/OSU-NLP-Group/HippoRAG 🧠 HippoRAG 2 流程概述 1. 离线索引(Offline Indexing) 在此阶段,HippoRAG 2 构建一个开放式知识图谱(Open KG)以存储知识…...

STM32配置系统时钟

1、STM32配置系统时钟的步骤 1、系统时钟配置步骤 先配置系统时钟&#xff0c;后面的总线才能使用时钟频率 2、外设时钟使能和失能 STM32为了低功耗&#xff0c;一开始是关闭了所有的外设的时钟&#xff0c;所以外设想要工作&#xff0c;首先就要打开时钟&#xff0c;所以后面…...

Docker 安装配置教程(配置国内源)

## 一、Windows 安装 Docker Desktop 1. 系统要求: - Windows 10 64位:专业版、企业版或教育版 - 必须开启 Hyper-V 和容器功能 - 至少 4GB 内存 2. 安装步骤: - 访问 Docker 官网下载 Docker Desktop - 双击安装程序 - 按照向导完成安装 - 重启电脑 ## 二、macOS 安装 Dock…...

016-C语言内存函数

C语言内存函数 文章目录 C语言内存函数1. memcpy2. memmove3. memset4. memcmp 注意&#xff1a; 使用这些函数需要包含 string.h头文件。 1. memcpy void * memcpy ( void * destination, const void * source, size_t num );从source指向的位置开始&#xff0c;向后复制num…...

[FPGA]设计一个DDS信号发生器

一、DDS DDS&#xff08;Data Distribution Service&#xff09; 是一种面向实时分布式系统的通信中间件标准&#xff0c;专为高性能、高可靠性、低延迟的数据传输场景设计。它由对象管理组织&#xff08;OMG&#xff09; 制定并维护&#xff0c;广泛应用于物联网&#xff08;…...

MySQL 线上大表 DDL 如何避免锁表(pt-online-schema-change)

文章目录 1、锁表问题2、pt-online-schema-change 原理3、pt-online-schema-change 实战3.1、准备数据3.2、安装工具3.3、模拟锁表3.4、解决锁表 1、锁表问题 在系统研发过程中&#xff0c;随着业务需求千变万化&#xff0c;避免不了调整线上MySQL DDL数据表的操作&#xff0c…...

脚本中**通配符用法解析

在文件路径匹配中&#xff0c;** 是一种特殊的通配符&#xff08;Glob Pattern&#xff09;&#xff0c;主要用于表示递归匹配任意层级的子目录。这种语法常见于以下场景&#xff1a; 1. 典型应用场景 .gitignore 文件&#xff1a; **/__pycache__ 表示匹配项目根目录下所有层…...

5 提示词工程指南-计划与行动

5 提示词工程指南-计划与行动 计划与行动 Cline 有两种模式: Plan 描述目标和需求、提问与回答、讨论、抽象项目的各个方面、确定技术路线、确定计划 计划与确认相当于架构师,不编写代码Act 按计划编写代码 按照计划编码Plan 模式的本质是构建实际编码前的上下文,Act 的本…...

62页华为IPD-MM流程:市场调研理论与实践方案精读【附全文阅读】

本文围绕市场调研展开,介绍其是联系市场和企业的纽带,具有收集陈述事实、解释信息、预测市场变化的作用,在 IPD 产品开发流程各阶段有不同应用。市场调研类型包括定性和定量研究,一般程序涵盖定义问题、拟定计划、抽样设计等多个环节。常用调研方法多样,各有特点和适用项目…...

(一)mac中Grafana监控Linux上的CPU等(Node_exporter 安装使用)

机器状态监控(监控服务器CPU,硬盘&#xff0c;网络等状态) Node_exporter安装在被测服务器上&#xff0c;启动服务 各步骤的IP地址要换为被测服务器的IP地址Prometheus.yml的 targets值网页访问的ip部分grafana添加数据源的URL 注意&#xff1a;只需要在被监听的服务器安装 n…...

STM32单片机入门学习——第44节: [13-1] PWR电源控制

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难&#xff0c;但我还是想去做&#xff01; 本文写于&#xff1a;2025.04.20 STM32开发板学习——第44节: [13-1] PWR电源控制 前言开发板说明引用解答和科普一…...

Windows 10 上安装 Spring Boot CLI详细步骤

在 Windows 10 上安装 Spring Boot CLI 可以通过以下几种方式完成。以下是详细的步骤说明&#xff1a; 1. 手动安装&#xff08;推荐&#xff09; 步骤 1&#xff1a;下载 Spring Boot CLI 访问 Spring Boot CLI 官方发布页面。下载最新版本的 .zip 文件&#xff08;例如 sp…...

WEMOS LOLIN32 开发板引脚布局和技术规格

&#x1f517; 快速链接ESP32 Development Boards, Sensors, Tools, Projects and More https://megma.ma/wp-content/uploads/2021/08/Wemos-ESP32-Lolin32-Board-BOOK-ENGLISH.pdf WEMOS LOLIN32 Development Board Details, Pinout, Specs WEMOS LOLIN32 Development Board …...

【AI 加持下的 Python 编程实战 2_07】第七章:基于 Copilot 完整演示问题分解能力在实际问题中的落地应用

【全新第二版《Learn AI-assisted Python Programming》封面】 写在前面 问题分解能力在 AI 辅助编程中具有举足轻重的作用。但是怎样分解才算合理有效呢&#xff1f;本章从一个具体的案例切入&#xff0c;完整展示了 GitHub Copilot 在自顶而下设计论的指导下圆满完成既定任务…...

React 路由入门秘籍:BrowserRouter 的江湖之道

前言 各位江湖中人,今日咱们聊一门流传在前端江湖的神秘绝学:<BrowserRouter>。此技出自 React 路由门派,专修客户端路由之道,擅长在 Web 世界中轻功跳转、闪转腾挪,悄无声息间掌控页面切换。 若你是构建现代 Web 应用的侠客,这篇秘籍堪比九阳真经,一经参悟,便…...

软件安装,systemctl命令,软连接

yum是centos里面的&#xff08;apt是Ubuntu的&#xff09; yum&#xff1a;RPM软件管理器&#xff0c;用于自动化安装配置Linux软件&#xff0c;并可以自动解决依赖问题 语法&#xff1a;yum 【-y】【install | remove | search | restart 】软件名称 -y&#xff1a;自动确…...

SpringBoot Actuator健康检查:自定义HealthIndicator

文章目录 引言一、Spring Boot Actuator健康检查概述二、自定义HealthIndicator的必要性三、实现自定义HealthIndicator四、高级健康检查配置五、实现自定义健康状态和响应码总结 引言 Spring Boot Actuator是Spring Boot框架中用于监控和管理应用程序的强大功能模块。它提供了…...