【新书推荐】11.1 子程序设计

news2024/11/17 11:33:13

第十一章 子程序及参数传递

         本章先讲述子程序设计的方法,然后介绍在子程序调用中参数的四种传递方法。

11.1 子程序设计

在前面的示例和练习中,会发现程序中会有一些反复使用到的代码片段。我们将程序中反复出现的程序片段设计成子程序,这样可以有效缩短程序长度,节约存储空间,大大减轻程序设计工作量。

在设计子程序时,需要注意到子程序的通用性、共享性和相对独立,方便阅读和调试修改。

在80x86系列汇编语言中,子程序常常以过程的形式出现。

本节内容:子程序的设计方法及举例。

过程调用和返回:过程调用和返回指令属于程序控制指令。过程调用指令CALL用于主程序转子程序,过程返回指令RET用于子程序转主程序。

过程定义语句

过程名     PROC  [NEAR  |  FAR]

.

.

.

过程名      ENDP

子程序举例:示例代码t11-1.asm~ t11-4.asm。

子程序说明信息

为了能正确地使用子程序,在定义子程序代码时需要做必要的说明。子程序的说明信息一般由以下几部分组成:

  1. 子程序名。
  2. 功能描述。
  3. 入口参数和出口参数。
  4. 使用的寄存器和存储单元。
  5. 使用的算法和重要的性能指标。
  6. 其他调用注意事项和说明。
  7. 调用实例。

定义子程序时,可以根据实际情况给出具体包含的信息。

示例代码:t11-5.asm

寄存器的保护与恢复:在子程序的代码中不可避免要使用一些寄存器或者存储单元存放内容。这样就会导致修改这些寄存器或存储单元原有的内容,当子程序返回主程序后,主程序将无法再使用这些寄存器或存储单元内原有的内容。

存储单元的保护:可以将数据备份到另一个地址处。

寄存器的保护有两种方法

第一种方法是主程序中将寄存器PUSH入栈保护,待子程序调用返回后再将寄存器POP出栈。

第二种方法是在子程序中将寄存器PUSH入栈,子程序返回前将寄存器POP出栈。

哪些寄存器需要做保护呢?凡是子程序中使用到的寄存器都需要做保护,但是作为出口参数的寄存器绝对不可以作保护。

11.1.1 过程调用和返回指令

过程调用和返回指令属于程序控制指令。过程调用指令用于主程序转子程序,过程返回指令用于子程序转主程序。由于程序的代码段可以是多个段组成,所以过程调用和过程返回指令又分为段间远调用、远返回指令和段内近调用、近返回指令。接下来我们介绍这两组指令的使用方法。

过程调用指令

(1)段内直接调用

CALL 过程名

CALL  subi                ;subi近过程

CALL  toascii            ;toascii近过程

CALL指令分解:

SP     <=  SP-2           ;分配2个字节堆栈空间

[SP]  <= IP                   ;压入偏移

IP      <= IP +DISP (地址差)

Disp 范围-32768~32767                                                                       

                                11-1 段内调用

subi和toascii为子程序名,子程序名就是其在代码段内的地址标号。近过程就是子程序和主程序在同一个代码段内,编译器编译时将子程序名替换为代码段内的偏移地址。

调用call指令时,需要先动态分配堆栈空间,将call指令后的下一条指令的16位偏移地址(称为返回地址)压入堆栈,因此栈顶指针寄存器SP-2,并且存入返回地址。

接着就是修改IP指令指针寄存器的值为子程序的偏移地址,实现主程序到子程序的跳转。因为16位汇编中,最大段为64KB,所以段内跳转的范围是-32768~32767。跳转的地址差值为:目的地址-返回地址。

(2)段内间接调用

CALL  oprd

SP     <=  SP-2         ;分配2个字节堆栈空间                                                                                  

[SP]  <= IP                   ;压入偏移

IP      <=  (OPRD)

oprd是16位通用寄存器或字存储器操作数,

存储子程序在段内的偏移地址。

注意指令分解执行的顺序

(3)段间直接调用

CALL     过程名

SP     <=  SP-2           ;分配2个字节堆栈空间

[SP]  <=  CS              ;压入返回地址的段值

SP     <=  SP-2           ;分配2个字节堆栈空间

[SP]  <=  IP               ;压入返回地址的偏移

IP      <=  跳转后子程序的偏移

CS     <=  跳转后子程序的段值                                                            

                                11-1 段间调用               

注意指令分解执行的顺序。                                                               

与无条件间接转移相似,机器指令中含有转移目标地址。

CALL  FAR  PTR  SUBRO   ;设SUBRO是远过程

CALL  SUBRO             ;设SUBRO是远过程

MASM汇编器是根据过程名所指定的被调用过程的类型决定段内还是段间调用。TASM汇编器是根据实际是否在同一段内判断的。

因为是跨段跳转,所以子程序名SUBRO表示为另一个代码段内的地址标号,该地址标号应该表示为段值:偏移,同理,返回地址也应表示为段值:偏移。

执行CALL指令调用时,首先需要动态分配4个字节的堆栈空间,先压入当前段的16位段值CS,再压入16位偏移,作为子程序的返回地址。

接着修改CS段寄存器和IP指令指针寄存器为远过程的段值和偏移,实现跨段跳转。

(4)段间间接调用

CALL OPRD

OPRD是双字存储器操作数,存储远过程调用的CS:IP。

SP     <=  SP-2           ;分配2个字节堆栈空间

[SP]  <=  CS              ;压入返回地址的段值

SP     <=  SP-2           ;分配2个字节堆栈空间

[SP]  <=  IP               ;压入返回地址的偏移

IP      <=  OPRD之低字值

CS     <=  OPRD之高字值

;注意指令分解执行的顺序

CALL   DWORD PTR  [BX]

CALL   VARD                 ;设VARD是双字变量

可以看出近调用和远调用的差异在于是否处理段寄存器。因为近调用发生在一个代码内的调用,因此不需要向栈中压入和切换代码段。而远调用因为发生在不同的代码段间,因此需要保存和切换代码段。但对于32位的windows,因为使用了平坦内存,在同一进程内的代码都是在一个大的4GB段中,因此不必再考虑段的差异,普通的应用程序使用的都是近调用,我们将在第三部分32位汇编中详细讲述。

过程返回指令

返回指令将返回地址从堆栈弹出到IP或CS:IP,从而实现子程序到主程序的跳转,主程序可以继续执行CALL指令后的下一条指令。返回指令也分为段内返回指令和段间返回指令。

(1)段内返回指令RET

RET指令分解:

IP      <=  [SP]          ;将返回地址偏移POP到IP寄存器

SP     <=  SP+2          ;释放返回地址偏移在栈内存储空间

(2)段间返回指令RET

RET指令分解:

IP      <=  [SP]          ;将返回地址偏移POP到IP寄存器

SP     <=     SP+2          ;释放返回地址偏移在栈内存储空间

CS     <=  [SP]          ;将返回地址段值POP到CS寄存器

SP     <=     SP+2          ;释放返回地址段值在栈内存储空间

虽然段内和段间返回指令助记符相同,但是它们的机器码是不同的。汇编器MASM根据RET指令所在过程的类型决定段间还是段内,TASM汇编器除此之外,还提供了专门的助记符RETF。

(3)段内带立即数返回指令

RET 表达式

指令分解:

IP               <=  [SP]            ;将返回地址偏移POP到IP寄存器

SP              <=SP+2              ;释放返回地址偏移在栈内存储空间

SP              <= SP+data       ;移动栈顶指针寄存器data个字节

由于堆栈正常操作都是以字为单位,所以Data及表达式的结果一般是偶数,如:RET 4。

(4)段间带立即数返回指令

RET    表达式

IP      <=  [SP]          ;将返回地址偏移POP到IP寄存器

SP     <=     SP+2          ;释放返回地址偏移在栈内存储空间

CS     <=  [SP]          ;将返回地址段值POP到CS寄存器

SP     <=     SP+2          ;释放返回地址段值在栈内存储空间

SP     <= SP+data       ;移动栈顶指针寄存器data个字节

例:RET  4

11.1.2 过程定义语句

子程序格式

过程名     PROC  [NEAR  |  FAR]

.

.

.

过程名      ENDP

PROC 指明该地址标号是过程名,即子程序。

NEAR表示该过程为近过程调用,FAR表示该过程为远过程调用。如果不指定过程类型,默认为NEAR型。

ENDP表示子程序定义结束。

示例

; 过程调用

;子程序名:htoasc

;功能:将一位十六进制数转换为对应的ASCII码子程序

;--------------------------------------------------------------------------

;设欲转换的十六进制数在AL的低4位

;转换得到的ASCII码在AL中

htoasc proc near

         and al,0fh

         add al,30h        ;先转换为ASCII字符

         cmp al,39h       ;再判断是否大于‘9’

         jbe htoasc1

         add al,7h

htoasc1:  

ret    ;调用返回

htoasc endp

;换一种写法

htoasc proc near

         and al,0fh

         cmp al,9            ;先判断是否大于9

         jbe htoasc1

         add al,37h        ;’A’~’F’

         ret

htoasc1:

         add al,30h

         ret

htoasc endp

;还可以写成

htoasc proc near

         and al,0fh

         cmp al,9

         jbe htoasc1

         add al,37h       

htoasc2:  

ret

htoasc1:  

add al,30h

         jmp htoasc2

htoasc endp

上述示例代码片段中可以包含一条ret返回指令,也可以包含多条ret返回指令。

11.1.3 子程序举例

例1:两位ASCII码十进制数转换为数值

动手实验78写一个程序将两位十进制数ASCII码转换为对应的十进制的子程序调用。

在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。

数据定义

data segment

num db 31h,30h

val db ?

data ends

算法分析:

设X为十位数,Y为个位数,计算10X+Y。

         示例代码51

;程序名:t11-1.asm                                   

;例1:写一个程序将两位十进制数ASCII码转换为对应的十进制的子程序调用 

;转换的算法为:设X为十位数,Y为个位数,计算10X+Y                                                    

;子程序名:subr    

;-----------------------------------------------------        

assume cs:code,ds:data

data segment

num db 31h,30h

val db ?

data ends                  

;

code segment

start:

         mov ax,data

         mov ds,ax

         ;入口参数

         mov dh,num[0]

         mov dl,num[1]

         ;子程序调用

         call subr

         mov val,al         ;保存返回值

         ;

         mov ax,4c00h

         int 21h

;------------------------------------------------------

;子程序名:subr

;功能:两位ASCII码十进制数转换为十进制

;入口参数:dh高位,dl低位

;出口参数:al

;其他说明:

subr proc

         ;保护寄存器

         PUSHF

         PUSH DX

         ;10X

         mov al,dh

         and al,0fh

         mov ah,10

         mul ah

         ;+Y

         mov ah,dl

         and ah,0fh

         add al,ah

         ;恢复寄存器

         POP DX    

         POPF

         ret    ;返回

subr endp

code ends

end start

;子程序可以换一种写法

subr proc

         mov al,dh

         sub al,30h

         mov ah,10

         mul ah

         mov ah,dl

         sub ah,30h

         add al,ah

         ret

subr endp

注意

1.上述程序未判断dh,dl是否是十进制数的ASCII码。

2.子程序参数传递方式为寄存器传参,将在“本章11.2节”详细讲述。

3.子程序说明信息包括子程序名、子程序功能、入口参数、出口参数和其他信息。

4.子程序中可能会改变FLAG和DX寄存器的值,因此使用PUSHF、PUSH DX、POP DX、POPF四条语句保护这两个寄存器,注意入栈和出栈的顺序(后进先出)。

5.子程序的定义必须包含在代码段内,即code ends伪指令之前。

         ●例2:16位二进制数转换为4位十六进制ASCII码

动手实验79写一个把16位二进制数转换为4位十六进制数ASCII码的子程序调用。

在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。

数据定义:

data segment

num dw 1234h

buff db ?,?,?,?,0dh,0ah,'$'

data ends

算法分析:

把16位二进制数向左循环移位四次,使高四位变为低4位,析出低四位调用子程序htoasc转换1位十六进制ASCII码,循环四次。

示例代码52

;程序名:t11-2.asm                                   

;例2:写一个把16位二进制数转换为4位十六进制数ASCII码的子程序    

;转换方法:把16位二进制数向左循环移位四次,使高四位变为低4位

;析出低四位调用子程序htoasc转换1位十六进制ASCII码,循环四次

;子程序名:htascs

;=================================================================     

assume cs:code,ds:data

data segment

num dw 1234h

buff db ?,?,?,?,0dh,0ah,'$';0dh,0ah为回车换行的ASCII值

data ends                  

code segment

start:

         mov ax,data

         mov ds,ax

         ;入口参数

         mov dx,num

         mov bx,offset buff

         ;

         call htascs

         ;显示转换后的ASCII字符串

         mov dx,offset buff

         mov ah,9

         int 21h

         ;

         mov ax,4c00h

         int 21h

;====================================================================

;子程序名:htascs

;功能:16位二进制数转换为4位十六进制数ASCII码

;入口参数:dx=欲转换的二进制数     

;ds:bx=存放转换得到的ASCII码串的缓冲区首地址        

;出口参数:十六进制数ASCII码串按高位到低位依次存放在指定的缓冲区中     

htascs proc      

         mov cx,4

htascs1:

         push cx

         mov cl,4

         rol dx,cl

         ;也可以使用下面4条语句实现移位

         ;rol dx,1

         ;rol dx,1

         ;rol dx,1

         ;rol dx,1

         mov al,dl

         call htoasc

         mov [bx],al

         inc bx

         pop cx

         loop htascs1

         ret

htascs endp     

;---------------------------------------         

;子程序名:htoasc

;功能:一位十六进制数转换为ASCII

;入口参数:al=待转换的十六进制数 

;出口参数:al=转换后的ASCII

htoasc proc near     

         and al,0fh

         add al,30h

         cmp al,39h

         jbe htoasc1

         add al,7h

htoasc1:

         ret

htoasc endp    

;-----------------------------------------

code ends

end start

上述示例入口参数为dx、bx寄存器,出口参数为指定的buff缓冲区。num为1234h,需要分别转换4个十六进制数,采用ROL循环移位的方法,从左往右析出每个十六进制数。接下来调用嵌套的子程序htoasc将其转换为十六进制数ASCII码。转换算法为:如果十六进制数在0~9之间,加30H,如果在A~F之间,再加7H。

注意:对于循环计数器CX的保护,因为子程序中“mov cl,4”语句会改变循环计数值,所以在改变前入栈保护,循环移位后再恢复cx的值,否则会导致死循环。

示例代码53

;程序名:T11-3.asm

;功能:利用上述子程序htascs按十六进制数形式显示地址为B000:0000H的字单元内容

;调用子程序htascs,htoasc

;------------------------------------

assume cs:code,ds:data

data segment

buff          db 4 dup(0)                         ;存放4位十六进制数的ASCII码串

                   db 'h',0dh,0ah,'$'               ;形成以$结尾的串

data ends

code segment

start:

         mov ax,data

         mov ds,ax

         ; 准备入口参数

         mov ax,0B000h

         mov es,ax

         mov dx,es:[0]                      ;取B000:0000h地址的内容

         mov bx,offset buff             ;准备入口参数

         call htascs                            ;调用子程序,转换4位十六进制数的ASCII码

         ;输出结果

         mov dx,offset buff

         mov ah,9

         int 21h

         ;

         mov ax,4c00h

         int 21h;

;-------------------------------------------

;子程序名:htascs

;功能:16位二进制数转换为4位十六进制数ASCII码

;入口参数:dx=欲转换的二进制数     

;ds:bx=存放转换得到的ASCII码串的缓冲区首地址        

;出口参数:十六进制数ASCII码串按高位到低位依次存放在指定的缓冲区中

htascs proc

         mov cx,4  ;循环计数

htascs1:  

         rol dx,1     ;循环左移4位

         rol dx,1

         rol dx,1

         rol dx,1

         mov al,dl           ;保存移位后dl的低4位值

         call htoasc        ;调用子程序

         mov [bx],al       ;保存到缓冲区

         inc bx                

         loop htascs1     ;重复循环4次

         ret                               

htascs endp                        

;-----------------------------------------

;子程序名:htoasc

;功能:一位十六进制数转换为ASCII

;入口参数:al=待转换的十六进制数 

;出口参数:al=转换后的ASCII

htoasc proc near

         and al,0fh

         add al,30h

         cmp al,39h

         jbe htoasc1

         add al,7h

htoasc1:  

         ret

htoasc endp

code ends

         end start

上述示例在调用子程序前,先准备好参数,dx =欲转换的二进制数, ds:bx=存放转换得到的ASCII码串的缓冲区首地址。出口参数为指定的buff缓冲区。获取指定地址B000:0000H的字单元内容的方法为es=B000H,然后mov dx,es:[0]。转换十六进制ASCII码的方法与例2相同,不再赘述。

例3:把16位二进制数转换为5位十进制数ASCII码的子程序

动手实验80写一个把16位二进制数转换为5位十进制数ASCII码的子程序,为了简单,二进制数为无符号数。

在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。

数据定义:

data segment

num dw 1234h

buff db 5 dup(?),24h

data ends

算法分析:

算法1:把16位二进制先转换为5位十进制BCD码,除以(10000,1000,100,10,1)然后再转换为ASCII码。        

算法2:把16位二进制数除以10余数为个位数的BCD码,商再除以10,余数为十位数的BCD码,循环5次。

示例代码54

         ;程序名:t11-4.asm                                   

;例3:写一个把16位二进制数转换为5位十进制数ASCII码的子程序,为了简单,二进制数为无符号数    

;子程序名:btoasc

;=================================================================     

assume cs:code,ds:data

data segment

num dw 1234h

buff db 5 dup(?),24h

data ends                  

code segment

start:

         mov ax,data

         mov ds,ax

         ;入口参数

         mov ax,num

         mov bx,offset buff

         ;子程序调用

         call btoasc

         ;输出结果

         mov dx,offset buff

         mov ah,9

         int 21h

         ;

         mov ax,4c00h

         int 21h

;=================================================================

;子程序名:btoasc

;入口参数:AX=欲转换的二进制数                                                            

;DS:BX=存放转换所得ASCII码串的缓冲区首地址                                                             

;出口参数:十进制数ASCII码串按万位到个位的顺序依次存放在指定的缓冲区         

btoasc proc     

         push si     

         push cx

         push dx

         push ax

         push bx

         mov si,5                      ;循环次数                                   

         mov cx,10                  ;除数10                             

btoasc1:                                                          

         xor dx,dx                    ;被除数扩展成32位                                 

         div cx                                                      

         add dl,30h                 ;余数为BCD码,转换为ASCII码                                     

         dec si                           ;循环次数减一                                   

         mov [bx][si],dl           ;ASCII码存入缓冲区                                           

         or si,si                          ;判断si是否为0                                

         jnz btoasc1                ;否,继续                

         pop bx

         pop ax

         pop dx

         pop cx

         pop si

         ret                                                 

btoasc endp                                                            

;SI即作为计数器,又作为变址指针使用,也可以不使用,inc bx实现变址

code ends

end start

注意

         示例代码54中使用的是算法2实现的。请读者独立实现算法1—将示例代码41封装为子程序。

11.1.4 子程序说明信息

为了能正确地使用子程序,在定义子程序代码时需要做必要的说明。子程序的说明信息一般由以下几部分组成:

1.子程序名。

2.功能描述。

3.入口参数和出口参数。

4.使用的寄存器和存储单元。

5.使用的算法和重要的性能指标。

6.其他调用注意事项和说明。

7.调用实例。

定义子程序时,可以根据实际情况给出具体包含的信息。

例4:把8位二进制数转换为2位十六进制数的ASCII

动手实验81写一个把8位二进制数转换为2位十六进制数的ASCII码的子程序,二进制数为无符号数。

在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。

数据定义:

data segment

num db 12h

buff db 2 dup(?)

data ends

算法分析:

         分别析出8位二进制数的高4位和低4位,并转换为十六进制数的ASCII码。

         示例代码55

;程序名:t11-5.asm                                   

;功能:把8位二进制数转换为2位十六进制数的ASCII码                                 

;=================================================================     

assume cs:code,ds:data

data segment

num db 12h

buff db 2 dup(?)

data ends         

code segment

start:

         mov ax,data

         mov ds,ax

         ;入口参数

         mov al,num

         mov bx,offset buff

         ;子程序调用

         call ahtoasc

         ;保存结果

         mov buff,ah

         mov buff+1,al 

         ;

         mov ax,4c00h

         int 21h

;=================================================================

;子程序名:ahtoasc      

;功能:把8位二进制数转换为2位十六进制数的ASCII码     

;入口参数:AL=欲转换的8位二进制数     

;出口参数:AH=十六进制数高位的ASCII码      

;AL=十六进制数低位的ASCII码  

;其他说明:1.近过程,2.除AX寄存器外,不影响其他寄存器3.调用了htoasc实现十六进制到ASCII码转换      

ahtoasc proc   

         mov ah,al

         shr al,1

         shr al,1

         shr al,1

         shr al,1

         call htoasc        ;高4位

         xchg ah,al

         call htoasc        ;低4位

         ret

ahtoasc endp

;---------------------------------------         

;子程序名:htoasc

;功能:一位十六进制数转换为ASCII

;入口参数:al=待转换的十六进制数 

;ds:bx=存放转换得到的ASCII码串的缓冲区首地址        

;出口参数:al=转换后的ASCII

htoasc proc near     

         and al,0fh

         add al,30h

         cmp al,39h

         jbe htoasc1

         add al,7h

htoasc1:

         ret

htoasc endp    

;-----------------------------------------      

code ends

end start

11.1.5 寄存器的保护与恢复

在子程序的代码中不可避免要使用一些寄存器或者存储单元存放内容。这样就会导致修改这些寄存器或存储单元原有的内容,当子程序返回主程序后,主程序将无法再使用这些寄存器或存储单元内原有的内容。这就需要我们在定义子程序时保护和恢复子程序中使用到的寄存器或存储单元存放的内容。

存储单元数据保护方法可以将数据备份到另外一个地址处作为副本使用。

寄存器的保护和恢复则有两种方法:

第一种方法:在主程序中将需要保护的寄存器push入栈,待子程序调用返回时,再pop出栈,恢复原值。

优点:只需要把寄存器压栈出栈。

缺点:不易理解,容易忘记,多次调用子程序会累赘。

第二种方法:在子程序中一开始就把子程序中要改变的寄存器压入堆栈,返回之前恢复。

优点:主程序调用时不需要考虑寄存器的恢复。特别是当子程序为第三方提供时,通常调用者并不清楚需要对哪些寄存器做保护。

         接下来我们以子程序btoasc为例。

举例

;子程序名:btoasc

;功能:一个16位二进制数转换为5位十进制数ASCII码串

;算法2:把16位二进制数除以10余数为个位数的BCD码,商再除以10,余数为十位数的BCD码,循环5次

;入口参数:AX=欲转换的二进制数

;DS:BX=存放转换所得ASCII码串的缓冲区首地址

; 出口参数:十进制数ASCII码串按万位到个位的顺序依次存放在指定的缓冲区

btoasc proc

         push ax

         push cx

         push dx

         push si

         mov si,5             ;循环次数

         mov cx,10         ;除数10

btoasc1:

         xor dx,dx           ;被除数扩展成32位

         div cx

         add dl,30h        ;余数为BCD码,转换为ASCII码

         dec si                  ;循环次数减一

         mov [bx][si],dl  ;ASCII码存入缓冲区

         or si,si                 ;判断si是否为0

         jnz btoasc1       ;否,继续

         pop si

         pop dx

         pop cx

         pop ax

         ret

btoasc endp

注意

1.上述子程序实际破坏了flag寄存器的部分标志,可以用pushf和popf指令保护和恢复标志寄存器,但是一般不在子程序中操作。

2.注意堆栈先进后出的特性。

3.有时为了简单,并不保护含有入口参数的寄存器,是否需要保护根据实际情况而定。

4.可以像寄存器一样通过入栈和出栈保护和恢复有关的存储单元的内容。但在子程序中尽量避免这样操做。子程序可以利用堆栈元素作为临时变量。

5.如果出口参数为寄存器传参,则不应对作为出口参数的寄存器做保护。

练习

1、怎样把程序片段封装为子程序?

2、请比较指令call与无条件转移指令jmp的异同。

3、段内调用指令是否可以调用远过程?如果可以,请举例说明。

4、是否可用过程返回指令ret调用某个过程?如果可能,请写出实例。

5、子程序说明信息应包含哪些内容?

6、比较寄存器保护和恢复的两种方法优缺点。

本文摘自编程达人系列教材《X86汇编语言基础教程》。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1485526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DM数据库学习之路(十九)DM8数据库sysbench部署及压力测试

sysbench部署 安装依赖 yum -y install make automake libtool pkgconfig libaio-devel vim-common 上传sysbench源代码 sysbench_tool.tar 测试是否安装成功 $ /opt/sysbench/sysbench-master-dpi/src/lua $ ./sysbench --version sysbench 1.1.0 sysbench测试DM 测试…

【STK】手把手教你利用STK进行仿真-STK软件基础02 STK系统的软件界面01 STK的界面窗口组成

STK系统是Windows窗口类型的桌面应用软件,功能非常强大。在一个桌面应用软件中集成了仿真对象管理、仿真对象属性参数、设置、空间场景二三维可视化、场景显示控制欲操作、仿真结果报表定制与分析、对象数据管理、仿真过程控制、外部接口连接和系统集成编程等复杂的功能。 STK…

MyBatis源码分析之基础支持层解析器

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨ &#x1f440;&#x1f440;&#x1f440; 个人博客&#xff1a;小奥的博客 &#x1f44d;&#x1f44d;&#x1f44d;&#xff1a;个人CSDN ⭐️⭐️⭐️&#xff1a;传送门 &#x1f379; 本人24应届生一枚&#xff0c;技术和水平有限&am…

录制用户操作实现自动化任务

先上视频&#xff01;&#xff01; 流程自动化工具-录制操作绘制流程 这个想法之前就有了&#xff0c;趁着周末时间给它撸出来。 实现思路 从之前的文章自动化桌面未来展望中已经验证了录制绘制流程图的可行性。基于DOM录制页面操作轨迹的思路监听页面点击、输入事件即可&…

蓝桥杯练习题——二分

1.借教室 思路 1.随着订单的增加&#xff0c;每天可用的教室越来越少&#xff0c;二分查找最后一个教室没有出现负数的订单编号 2.每个订单的操作是 [s, t] 全部减去 d #include<iostream> #include<cstring> using namespace std; const int N 1e6 10; int d[…

Linux之相对路径、绝对路径、特殊路径符

相对路径和绝对路径 cd /root/temp 绝对路径写法 cd temp 相对路径写法 1、绝对路径&#xff1a;以根目录为起点&#xff0c;描述路径的一种写法&#xff0c;路径描述以 / 开头。 2、相对路径&#xff1a;以当前目录为起点&#xff0c;路径描述无需以 / 开头。 特殊路径符 如…

cRIO9040中NI9871模块的测试

硬件准备 CompactRIO9040NI9871直流电源&#xff08;可调&#xff09;网线RJ50转DB9线鸣志STF03-R驱动器和步进电机 软件安装 参考&#xff1a;cRIO9040中NI9381模块的测试 此外&#xff0c;需安装NI-Serial 9870和9871扫描引擎支持 打开NI Measurement&#xff06;Automa…

C++之set、multiset

1、set简介 set是一种关联式容器&#xff0c;包含的key值唯一&#xff0c;所有元素都会在插入时自动被排序&#xff0c;其存储结构为红黑树。set只能通过迭代器(iterator)访问。 set和multiset的区别&#xff1a; &#xff08;1&#xff09;set不允许容器中有重复的元素&…

C++_程序流程结构_选择结构_if

程序流程结构 C/C支持最基本的三种程序运行结构&#xff1a;顺序、选择、循环结构 顺序结构&#xff1a;程序按顺序执行&#xff0c;不发生跳转选择结构&#xff1a;依据条件是否满足&#xff0c;有选择地执行相应功能循环结构&#xff1a;依据条件是否满足&#xff0c;循环多…

跟 AI 学 StarRocks:简介

因为要支持公司的 BI 建设&#xff0c;团队引入了 StarRocks 数据库&#xff0c;此前我没有了解过此项技术&#xff0c;不过因为有架构师引入了此项技术栈&#xff0c;就顺便学习一下。 一、什么是 MPP 数据库&#xff1f; MPP 数据库指的是大规模并行处理&#xff08;Massiv…

zotero | 多平台同步 | 坚果云

zotero注册登陆 打开zotero软件&#xff0c;mac电脑打开首选项&#xff0c;如下图所示&#xff1a; 然后点击同步选项&#xff0c;如下图所示&#xff0c;如果已经有账号&#xff0c;请登陆账号&#xff0c;无则注册账号之后再登陆&#xff1b; 注册坚果云账号 注册完坚果…

QT6 libModbus 用于ModbusTcp客户端读写服务端

虽然在以前的文章中多次描述过,那么本文使用开源库libModbus,可得到更好的性能&#xff0c;也可移植到各种平台。 性能&#xff1a;读1次和写1次约各用时2ms。 分别创建了读和写各1个连接指针&#xff0c;用于读100个寄存器和写100个寄存器&#xff0c;读写分离。 客户端&am…

文心一言 VS 讯飞星火 VS chatgpt (206)-- 算法导论15.4 3题

三、设计 LCS-LENGTH的带备忘的版本&#xff0c;运行时间为O(mn)。需要写代码的时候&#xff0c;请用go语言。 文心一言&#xff0c;代码正常运行&#xff1a; LCS-LENGTH&#xff08;Longest Common Subsequence Length&#xff09;问题的带备忘的版本通常指的是使用动态规划…

备考2024年小学生古诗文大会:历年真题15题练习和独家解析

如何提高小学生古诗词的知识&#xff1f;如何激发小学生古诗词的学习兴趣&#xff1f;如何提高小学古诗词的学习成绩&#xff1f;如何备考2024年小学生古诗文大会&#xff1f;... 如果你也在关注这些问题&#xff0c;我的建议是参加每年一度的小学生古诗词大会&#xff08;免费…

金融行业专题|期货超融合架构转型与场景探索合集(2023版)

更新内容&#xff1a; 更新 SmartX 超融合在期货行业的覆盖范围、部署规模与应用场景。新增 CTP 主席系统实践与评测、容器云资源池等场景实践。更多超融合金融核心生产业务场景实践&#xff0c;欢迎下载阅读电子书《SmartX 金融核心生产业务场景探索文章合集》。 面对不断变…

云计算与大数据课程笔记(一)云计算背景与介绍

如何实现一个简易搜索引擎&#xff1f; 实现一个简易的搜索引擎可以分为几个基本步骤&#xff1a;数据收集&#xff08;爬虫&#xff09;、数据处理&#xff08;索引&#xff09;、查询处理和结果呈现。下面是一个概括的实现流程&#xff1a; 1. 数据收集&#xff08;爬虫&am…

Android和Linux的嵌入式开发差异

最近开始投入Android的怀抱。说来惭愧&#xff0c;08年就听说这东西&#xff0c;当时也有同事投入去看&#xff0c;因为恶心Java&#xff0c;始终对这玩意无感&#xff0c;没想到现在不会这个嵌入式都快要没法搞了。为了不中年失业&#xff0c;所以只能回过头又来学。 首先还是…

vue实现水印功能

目录 一、应用场景 二、实现原理 三、详细开发 1.水印的实现方式 2.防止用户通过控制台修改样式去除水印效果&#xff08;可跳过&#xff0c;有弊端&#xff09; 3.水印的使用 &#xff08;1&#xff09;单页面/全局使用 &#xff08;2&#xff09;全局使用个别页面去掉…

MySQL 常用优化方式

MySQL 常用优化方式 sql 书写顺序与执行顺序SQL设计优化使用索引避免索引失效分析慢查询合理使用子查询和临时表列相关使用 日常SQL优化场景limit语句隐式类型转换嵌套子查询混合排序查询重写 sql 书写顺序与执行顺序 (7) SELECT (8) DISTINCT <select_list> (1) FROM &…

18.题目:编号760 数的计算

题目&#xff1a; ###该题主要考察递推、递归 将该题看成若干个子问题 #include<bits/stdc.h> using namespace std; const int N20; int a[N];int dfs(int dep){int res1;for(int i1;i<a[dep-1]/2;i){a[dep]i;resdfs(dep1);}return res; }int main(){int n;cin>…