100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【微机原理与接口技术】学习笔记4 汇编语言程序设计

【微机原理与接口技术】学习笔记4 汇编语言程序设计

时间:2020-01-23 06:42:35

相关推荐

【微机原理与接口技术】学习笔记4 汇编语言程序设计

文章目录

4.1 汇编语言程序格式和伪指令4.1.1 汇编语言程序格式(略)4.1.2 伪指令语句1. 段定义语句2. 段分配语句3. 过程定义语句4. 变量定义语句5. 程序结束语句6. 其它伪指令 (略)4.1.3 完整的汇编语言程序框架1. 完整的汇编语言程序框架2. 堆栈的设置3. 返回DOS操作系统4.2 DOS系统功能调用和BIOS中断调用4.2.1 概述(略)4.2.2 DOS系统功能调用1. 中断处理程序分类2. DOS系统功能调用方法3. DOS系统功能调用举例1)DOS键盘功能调用4.2.3 BIOS中断调用1. 键盘中断调用INT 16H1)0号功能调用2)1号功能调用4.3 汇编语言程序设计方法与实例4.3.1 顺序结构程序设计4.3.2 分支程序设计4.3.3 循环结构程序4.3.4 代码转换程序(略)4.3.5 过程调用 ★★★

汇编语言的汇编处理过程:

1)按语法规则编写源程序PROG.ASM

2)用汇编程序将源程序翻译成目标文件PROG.OBJ

3)用连接程序对1个或几个.OBJ模块连接后,生成能在机器上执行的程序PROG.EXE

如果汇编过程中出错,要在纠错后重新汇编;连接过程也会出现新的错误,需要反复修改。

4.1 汇编语言程序格式和伪指令

4.1.1 汇编语言程序格式(略)

4.1.2 伪指令语句

1. 段定义语句

段定义语句SEGMENTENDS,用来定义一个逻辑段

例 用段定义语句定义一个数据段, 段名为DATA, 段中包含X、Y两个变量。

DATA SEGMENT;数据段开始,DATA为段名;表示该段的基址X DW 1234H ;变量X的段基址:偏移量;=DATA:0000 内容为1234HY DB 56H;变量Y的段基址: 偏移量;=DATA:0002 内容为56HDATA ENDS;数据段结束

段定义语句的一般形式:

段名 SEGMENT [定位类型] [组合类型] ['分类名']PAGE(页) *NONE 'STACK'*PARA(节) PUBLIC 'CODE'WORD(字) STACKBYTE(字节) COMMONATMEMORY;段中内容段名 ENDS

[ ]项可省略,但堆栈段的组合类型STACK,不可省略。省略项不写时,其值用带*的项,它们是隐含用法,用的是默认值

2. 段分配语句

段分配语句ASSUME告诉汇编程序,4个段寄存器CS、DS、SS、ES分别与哪些段有关。格式如下,也可分两行书写。

ASSUMECS:代码段名, DS:数据段名, SS:堆栈段名, ES:附加段名

3. 过程定义语句

结构和功能相同,仅有一些变量赋予的值不同的程序段独立编写,用过程定义伪指令PROCENDP进行定义,并把这些程序段称为过程(Procedure)或子程序,由主程序中的CALL语句来调用它们。

过程定义格式:

过程名 PROC [NEAR]/FAR...;过程内容RET过程名 ENDP

PROC伪语句中,必须说明是近过程NEAR还是远过程FARNEAR可以省略不写。在过程内部必须安排一条返回指令RETRET n,以便返回主程序。

过程像标号一样,有3种属性:段基址偏移地址和距离属性NEARFAR),它可作为CALL指令的操作数。

用CALL语句调用过程,无需说明是近调用还是远调用。例如:

CALL 过程名

4. 变量定义语句

变量定义语句的一般形式为:

变量名 伪指令指示符 操作数;注释

说明如下:

变量名用符号表示,也可以省略。伪指令包括DB、DW、DD、DQDT,分别定义字节、字、双字、4字10字节变量。操作数可以有具体的字节、字和双字等初值也可以不指定具体数值,而用一个问号?来表示,此时仅为变量留出存储单元。在数据段或附加数据段中用伪指令定义

[变量名] DB 表达式[, ……] ;定义字节变量[变量名] DW 表达式[, ……] ;定义字变量[变量名] DD 表达式[, ……] ;定义双字变量[变量名] DQ 表达式[, ……] ;定义四字变量

例 变量定义语句举例。

FIRST DB ?;定义一个字节变量, 初始值不确定SECOND DB 20H, 33H;定义两个字节变量THIRD DW 1122H, 3344H ;定义两个字变量FOUR DQ 12345678H;定义一个双字变量

还可用复制操作符DUP来定义重复变量,其格式为:

变量名 伪指令指示符 n DUP (操作数)

其中n为重复变量的个数。

例 用重复操作符DUP定义变量。

N1 DB 100 DUP (?) ;分配100个字节单元,初值不确定N2 DW 10 DUP (0) ;定义10个字单元,初值均为0N3 DB 100 DUP (3 DUP(8), 6);定义100个"8, 8, 8, 6"的数据项

数据项也可写成单个字符或字符串的形式,通常用字节来表示。 如DB 'Welcome'在内存中顺序存放各字符的ACSII码。

例 如数据在存储单元中的存放形式如图,试给出相应的变量定义语句。

DATA1 DB '3', 'A'DATA2 DW 98, 100H, -2DATA3 DD 12345678HDATA4 DB 100 DUP (0)

5. 程序结束语句

程序结束语句的格式为:END [标号名或名字]它位于程序的最后一行,指示源程序结束,遇到END伪指令则停止汇编。标号名或名字可省略。

6. 其它伪指令 (略)

4.1.3 完整的汇编语言程序框架

完整的汇编语言程序包含数据段、代码段、堆栈段和附加数据段。其中:

代码段是必须要有的;堆栈段根据情况设置;代码段中要用到变量或数据时,应该设置数据段;当代码段中有字符串操作指令时,不仅要设置数据,还必需设置附加段,而且必须将源串存放在数据段中,而把目的串放在附加段中。

下面先给出程序框架,再介绍如何设置堆栈段,以及程序结束后怎样返回DOS操作系统。

1. 完整的汇编语言程序框架

例 汇编语言程序框架。

DATASEGMENT ;数据段X DB?Y DW ?DATAENDS;EXTRASEGMENT ;附加段ALPHA DB ?BETA DW ?EXTRAENDS;STACKSEGMENT PARA STACK 'STACK' ;堆栈段STACK绝不可少STAPN DB 100 DUP(?) ;定义100字节空间TOP LABEL WORD ;TOP为字类型,偏址100,第100个字节处(TOP不代表任何变量,是栈底)STACKENDS;代码段CODESEGMENT MAINPROC FAR ;过程定义语句 ASSUME CS:CODE, DS:DATA, ES:EXTRA, SS:STACK ;说明4个段寄存器分别与哪些段有关START: MOV AX, STACK;设堆栈段寄存器SS:SPMOV SS, AXMOV SP, OFFSET TOPPUSH DS;DS入栈保护SUB AX, AX;AX=0PUSH AX;段内偏移量“0”入栈MOV AX, DATA ;AX数据段基址DATAMOV DS, AX;DS数据段基址DATAMOV AX, EXTRAMOV ES, AX ;ES附加段基址EXTRA..... ;用户要编写的程序内容RET ;返回DOSMAINENDP ;MAIN过程结束CODE ENDS;代码段结束END START ;整个源代码结束

代码段、数据段、附加段和堆栈段,都用段定义伪指令SEGMENTENDS定义。数据段或附加段,用DB、DW等伪指令设置实际数值。堆栈段定义了100字节空间,其数值也可修改

代码段用来存放可执行的指令序列。这里用PROC FARENDP伪指令将整个程序编写成一个远过程的形式,过程名为MAIN。最后一条指令语句为过程返回指令RET,使程序执行完后返回到调用它的地方。

MAIN过程中,首先用段分配伪指令ASSUME告诉汇编程序,4个段寄存器分别与哪些段相对应,但不能将段基地址装入相应的段寄存器中,还要给DSESSS寄存器赋初值,CS则由操作系统赋初值。对于堆栈段,要给SSSP赋初值,以设定堆栈。

设置堆栈后,紧接着用下面3条指令,将DS推入堆栈保护起来,再使00H入栈,以便在程序结束时,能执行RET指令来返回DOS,即:

PUSH DS;DS入栈SUB AX, AXPUSH AX;00H入栈

用户编写的程序的具体内容,放在初始化程序之后RET指令之前。代码段之后,再安排一条END START指令,汇编程序遇到这条指令后就结束汇编,并自动从START开始往下执行程序。

2. 堆栈的设置

如果程序中没有定义堆栈段,连接时会给出一个警告信息:Warning: no stack segment。此错误不影响连接过程的完成,这时,DOS会自动定义一个堆栈段,使程序仍可正常运行。

3. 返回DOS操作系统

汇编语言程序在DOS下运行结束后,应能正确返回DOS,否则其它程序将无法运行,还会导致死机。返回DOS3种方法:

1)按程序框架设定的方法返回。先将主程序定义为一个远过程,再执行3条指令:

PUSH DSSUB AX, AXPUSH AX...RET

DS00H推入栈,再执行RET指令,转去执行INT 20H指令,返回DOS。这是返回DOS的常规方法

2)执行4CH号DOS功能调用。程序结束前按如下方法使用4CH号DOS功能调用指令,返回DOS。

MOVAX, 4C00H ;AH=4CH, 是DOS功能号;AL通常置为0INT21H

这种方法功能更强,更安全,使用也比较方便,建议使用这种方法返回DOS

3)若编写的程序要以.COM文件的形式执行,可用INT 20H指令直接返回DOS

4.2 DOS系统功能调用和BIOS中断调用

4.2.1 概述(略)

4.2.2 DOS系统功能调用

1. 中断处理程序分类

8086 CPU可处理256类中断,利用INT n指令,可直接调用256个系统已编写好的中断处理程序INT n指令中的类型号n=00~FFH

n=00~04H专用中断,处理除法错、单步、不可屏蔽中断NMI、断点、溢出中断;n=10H~1AH、2FH、31H、33HBIOS中断,即保存在系统ROM BIOS中的BIOS功能调用。n=20H~2EHDOS中断,应用DOS提供的功能程序来控制硬件,可对显示器、键盘、打印机、串行通信字符设备提供输入输出服务。例如:n=20H程序结束中断,利用INT 20H中断可返回DOS操作系统n=21H则为功能最强大的DOS中断,它包含了很多子功能,给每个子功能程序赋一个编号,称为功能号,调用前要送到AH寄存器中

2. DOS系统功能调用方法

DOS系统功能调用的步骤:

功能调用号送到AH寄存器中,AH=00~6CH入口参数送到指定的寄存器中,一种功能调用又包含多个子功能,有些调用不带参数。执行INT 21H指令。得到出口参数,或将结果显示在CRT上。

DOS系统功能调用表如下:

3. DOS系统功能调用举例

1)DOS键盘功能调用

利用DOS功能调用,可将读入的键值送进AL,并显示在CRT上,或检查是否有键压下等,还可将从键盘输入的一串字符输入到内存缓冲区中

例 DOS功能调用01H等待从键盘输入一个字符

MOVAH, 01H;AH功能调用号01HINT21H;AL读入键值, 并显示该字符

若有键压下,读入键值, 并检查是否为Ctrl-Break键?若是,自动调用INT 23H中断, 执行退出命令;否则将键值送入AL,并显示该字符

例 交互式程序中,用户键入字母键YN,分别转入不同的程序去处理,并在CRT上显示键入字符; 若按了Ctrl-Break,则结束程序,否则继续等待。

GET_KEY:MOVAH, 01H ;AH功能调用号01HINT21H ;AL读入键值CMPAL, 'Y' ;键值是Y吗?JE YES ;是,转YESCMP AL, 'N' ;不是Y,是N吗?JE NO ;是,转NOJNE GET_KEY ;不是N,返回继续等待YES: ... ;按Y键的处理程序NO: ... ;按N键的处理程序

例 DOS功能调用06H,控制台I/O(控制台指键盘和CRT),不检查是否按了Ctrl-Break键。

MOV AH, 06H;6号功能调用MOV DL, 0FFH;DL=FFH, 键盘输入INT 21H

当调用时DL=FFH,表示从键盘输入字符。执行后,若ZF=0,则AL中为输入字符的键值;若ZF=1,表示无键压下,AL中不是键值。

如果调用时DL≠FFH,表示从屏幕输出字符。

4.2.3 BIOS中断调用

有些DOS系统功能调用和BIOS中断调用能完成同样的功能。例如,要打印一个字符,可以用INT 21H5号DOS功能调用,也可用BIOSINT 17H0号中断调用。

由于BIOS更接近硬件,使用起来要复杂一些,尽量使用DOS系统功能调用。有些情况下,必须使用BIOS中断调用。例如,INT 17中断的2号调用为读打印机状态,DOS功能调用无这种功能,只能使用BIOS中断调用。

ROM BIOS中断调用的方法与DOS系统功能调用法类似,不过每个中断调用可能会包含多个子功能用功能号来区分它们。BIOS中断调用的基本步骤为:

功能号送AH设置入口参数执行INT n指令分析出口参数及状态

下面介绍几种BIOS中断调用。

1. 键盘中断调用INT 16H

这种类型的中断调用有3种功能,功能号为0,1,2,调用前,需将功能号送到AH中。

1)0号功能调用

功能: 从键盘读入一个字符。

例 编写从键盘读入一个字符的程序段。

MOV AH, 0 ;功能号0INT16H ;等待键盘输入

键盘上的键用28位数值进行标记:

最高位b7决定该键是压下还是松开b7=0,表示该键压下,b7=1,表示键已松开。后7位是这样定义的: 对于有ASCII码的键来说,第一字节为ASCII码第二字节为键盘扫描码,后者由系统根据键的位置确定;对于无ASCII码的键来说,第一字节为0第二字节为扩展码

这样,利用INT 16H0号功能调用,就可知道是哪个键压下了或松开了。

2)1号功能调用

查询键盘缓冲区,对键盘扫描,但不等待

例 编程查看键盘缓冲区。

MOV AH, 1 ;功能号1INT 16H

调用结果:

ZF=0键盘缓冲区不空,有键按了,AL=键入字符ASCII码AH=扫描码;ZF=1缓冲区空

4.3 汇编语言程序设计方法与实例

4.3.1 顺序结构程序设计

如果用循环程序将00~FFH先后送入DL,再利用DOS的2号功能调用,则可显示全部的标准和扩展ASCII码,包括全部控制符以及积分符、希腊字母等。

例 由人机对话从键盘输入110进制数(0~9),查表求键入数字的平方值,存入AL寄存器中,并显示有关的提示信息。试编写汇编语言程序。

解:

数据段中,先给出数字0~9的平方值,逐个存入TABLE开始的内存中,形成表格,以便查找,再给出等待显示的提示信息。

代码段由3个部分组成:显示提示信息等待键入数字查表求键入数字的平方值,并将结果存入AL中。

程序如下:

DATASEGMENTTABLE DB 0, 1, 4, 9, 16, 25, 36, 49, 64, 81;数字0~9的平方值BUF DB 'Please input a number(0~9):', 0DH, 0AH, '$' ;提示信息DATAENDSCODESEGMENTASSUME CS:CODE, DS: DATASTART:MOVAX, DATAMOVDS, AX;设置DSMOVDX, OFFSET BUF;设置DX,使字符串首地址=DS:DXMOVAH, 9H;9号DOS功能调用INT21H;显示提示信息MOVAH, 01H;1号功能调用,等待键入字符INT21H;AL=键入数字的ASCII码ANDAL, 0FH;AL=截下数字值;(表内元素序号)MOVBX, OFFSET TABLE ;BX指向表头地址TABLEMOVAH, 0 ;AX寄存器高字节清0ADDBX, AX ;表头地址+键入数字(AL),结果存入BXMOVAL, [BX] ;查表求得平方值MOVAX, 4C00HINT21H ;返回DOSCODEENDSENDSTART

4.3.2 分支程序设计

下面介绍一个比较复杂的分支程序,其中也包含了循环程序。

例 在存储器中以首地址BUF开始存有一串字符,字符串个数用COUNT表示。要求统计数字0~9、字母A~Z其它字符的个数,并分别将它们的个数存储到NUM开始的3个内存单元中去。

在ASCII码表中,数字0-9的ASCII码为30H~39H,大写字母A~Z的ASCII码为41H~5AH,其余值为其它字符或控制符的ASCII码值。可以将ASCII码分成5个部分或5个分支来处理,其示意图如下

先从BUF单元取出1个字符的ASCII码,经分支程序判断它属于数字、字母还是其它字符,然后使相应计数器的值+1

数字个数存放在DL中,字母个数存放在DH中。

接下来分析第2个数,直至所有字符处理完后,将统计出的个数送入相应存储单元

DATASEGMENTBUFDB '+36', 'PRINT', 'abc', '2A0CH', '#' ;一串字符COUNT EQU $-BUF;$=当前地址, COUNT=字符总个数NUMDB3 DUP(?);先后存放存数字、字母和其它字符个数DATAENDSCODE SEGMENTASSUME CS:CODE, DS:DATASTART:MOV AX, DATAMOV DS, AX;设置数据段MOV CH, COUNT ;CH=数组长度MOV BX, 0 ;BX为基址指针,初值清0MOV DX, 0 ;DH数字个数,DL字母个数,初值清0LOOP1: MOV AH, BUF[BX];AH取一个数CMP AH, 30H;<30H?JL NEXT ;①是,转CMP AH, 39H;>39H?JG ABC ;是,转INC DH ;②否,数字个数增1JMP NEXTABC: CMP AH, 41H ;<41H?JL NEXT ;③是,非字母,转CMP AH, 5AH ;>5AH?JG NEXT ;⑤是,非字母,转INC DL ;④否,字母个数增1NEXT: INC BX ;基地址指针加1DEC CH ;字符串长度减1JNZ LOOP1;未完,取下一个数

4.3.3 循环结构程序

例 在一串给定个数的数据中寻找最大值,存放到MAX存储单元中。

DATA SEGMENTBUFDW 1234H, 3200H, 4832H, 5600H ;一串字数据COUNT EQU ($-BUF)/2;数据个数(循环次数)MAXDW ? ;存最大值DATA ENDS;STACK SEGMENT 'STACK'STAPN DB 100 DUP(?)TOPEQU LENGTH STAPNSTACK ENDS;CODE SEGMENTMAIN PROC FARASSUME CS:CODE, SS:STACKSTART: MOV AX, STACKMOV SS, AXMOV SP, TOPPUSH DSSUB AX, AXPUSH AXMOV AX, DATAMOV DS, AXMOV CX, COUNT ;CX 字符个数LEA BX, BUF ;BX = BUF的偏移地址MOV AX, [BX] ;AX = 缓冲器中取一个数DEC CX ;循环次数减1AGAIN: INC BX ;修改地址指针INCBXCMP AX, [BX];AX与后取的数比较JGE NEXT ;如AX中数大于等于后者,则转MOV AX, [BX];如后取的数大,则将其送AXNEXT: LOOP AGAIN ;没处理完,转(循环操作)MOV MAX, AXRET ;返回DOSMAIN ENDP ;处理完,结束CODE ENDSENDMAIN

本例通过LOOP指令执行循环操作,取字符串的地址指针BX要用指令修正,以指向下个字单元取数进行比较。

例 有一个无符号数组共含5个元素:12, 7, 19, 8, 24, 它们存放在LIST开始的字单元中,编程将数组中的数按从大到小的次序排列(元素个数n=5)。

编程思路:

编程时采用冒泡法排序。比较从第1个元素开始,与相邻数比较,若大的在前小的在后,次序就排好了,不要交换,否则交换。然后将小的数与第3个元素比较,经n-1(=4)次比较后,一行中最小的元素7排到了最后面。共循环比较了n-1(=4)次。再作第二轮比较,这轮只要比较n-2(=3)次,即可将数组中的数按从大到小的次序排列好

这是一个多重循环程序。程序稍加修改后,即可实现从小到大的排序

DATASEGMENTLISTDW 12,7,19,8,24 ;数组字单元COUNTEQU ($-LIST)/2 ;数组长度n=10/2=5DATAENDS;SORTSEGMENTASSUME CS: SORT, DS: DATABEGIN: MOV AX, DATAMOV DS, AXMOV CX, COUNT-1 ;CX比较轮数(大循环次数n-1)LOOP1: MOV DX, CX ;DX大循环次数MOV BX, 0 ;地址指针LOOP2: MOV AX, LIST[BX] ;AX=LIST(i)CMP AX, LIST[BX+2];LIST(i)≥LIST(i+2)?JAE NO_CHANGE ;是, 转XCHG AX, LIST[BX+2];否, 交换MOV LIST[BX], AX;使大数在前NO_CHANGE: ADD BX, 2;BX增2, 取下个数LOOP LOOP2;一轮没比完, 转, CX减1, 继续比MOV CX, DX;一轮比完, CX=比较轮数LOOP LOOP1;CX=CX-1, 非0则转下轮比较MOV AX, 4C00H;比完, 返回DOSINT 21HSORTENDSENDBEGIN

例 折半查找算法:

data segmentarraydw 12,11,22,33,44,55,66, ;12是数组元素个数77,88,99,111,222,333number dw 55low_idx dw ?high_idx dw ?data ends...lea di, arraymov ax, number ;要查找的数55cmp ax, [di+2] ;(ax)与第一个元素比较11ja chk_lastlea si, [di+2]je exit ;(ax)=第一个元素,找到退出stcjmp exit ;(ax)<第一个元素,未找到退出chk_last:mov si, [di] ;元素个数12shl si, 1 ;计算最后一个元素add si, di;的地址cmp ax, [si] ;(ax)与最后一个元素比较jb search;<最后一个元素, 进入searchje exit ;(ax)=最后一个元素,找到退出stcjmp exit ;(ax)>最后一个元素,未找到退出search:mov low_idx, 1mov bx, [di];元素个数mov high_idx, bx ;元素个数mov bx, di ;数组首地址mid:mov cx, low_idx ;折半查找的leftmov dx, high_idx ;折半查找的rightcmp cx, dx ja no_match;找不到, 退出add cx, dx ;left+rightshr cx, 1 ;(left+right)/2mov si, cx shl si, 1compare:cmp ax, [bx+si] ;要查找的数55和arr[mid]进行比较je exit ;相等退出ja higher ;大于dec cxmov high_idx, cx jmp mid ;进行下一轮折半higher:inc cxmov low_idx, cxjmp midno_match:stcexit:...

4.3.4 代码转换程序(略)

4.3.5 过程调用 ★★★

例 内存中有两个数组ARY1ARY2,数组长度为2010,要求编写一个程序,分别累加两个数组的值,存入SUM1SUM2开始的单元中,低字节在前,高字节在后

累加第1个数组值时,要做20次加法,加法可用子过程实现;累加第2个数组时,要做10次加法,加法也可调相同的子过程来完成,但两次调用前的入口参数和存放结果的单元不同。

DATA SEGMENT ;数据段ARY1 DB 20 DUP(?) ;数组1,20个随机数SUM1 DB 2 DUP(?) ;存数组1各数相加之和ARY2 DB 10 DUP(?) ;数组2,10个随机数SUM2 DB 2 DUP(?);存数组2相加之和DATA ENDSSTACK SEGMENT STACK;堆栈段DW 50 DUP (?)TOP LABEL WORDSTACK ENDSCODE SEGMENT;代码段MAIN PROC FAR;主程序ASSUME CS:CODE, DS:DATA, SS:STACKBEGIN: MOVAX, STACK;设置堆栈段SS:SPMOVSS, AXMOVSP, OFFSET TOPPUSH DSSUBAX, AXPUSH AXMOVAX, DATA ;设置数据段MOVDS, AX;对第一个数组求和LEASI, ARY1 ;转子程序前入口参数, SI=ARY1首地址MOVCX, LENGTH ARY1;CX=ARY1长度MOVBX, OFFSET SUM1;BX=和单元首址CALL SUM ;转子过程,求数组1之和;对第二个数组求和LEASI, ARY2 ;转子程序前设ARY2之入口参数MOVCX, LENGTH ARY2MOVBX, OFFSET SUM2CALL SUM ;转子过程,求数组2之和RET ;返回DOSMAIN ENDP ;主程序结束;;子程序SUM;SUMPROC NEAR;求和子过程SUMXOR AL, AL;AX清0,CF标志清0MOV AH, 0;AH存进位,初值清0LOOP1: ADC AL, [SI] ;数组中取一元素,;带进位累加到ALADCAH, 0;进位累加到AH中INC SI;修改地址指针LOOP LOOP1;未完,继续MOV [BX], AL;已处理完,存和数, 低地址字节在前MOV [BX+1], AH;存进位累加值; 高地址字节在后RETSUMENDP;SUM子过程结束;CODE ENDSENDMAIN;整个程序结束

从上面的示例中,我们看到,程序通过寄存器来向子过程中传递参数,做法没问题,但是不能代表典型的做法——比如说,子过程的参数太多以至于寄存器不够用;传递的参数是一个结构体等等……我们需要知道一个通用的规则,用于向子过程传递多种类型的、数量不定的参数——现实的做法,是通过堆栈传送参数地址。

累加数组中的元素:

data segmentary dw 10,20,30,40,50,60,70,80,90,100count dw 10sum dw ?data endsstack segmentdw 100 dup (?)tos label word ;栈底和初始的栈顶stack endscode1 segmentmain proc farassume cs:code1, ds:data, ss:stackstart:mov ax, stack ;设置堆栈段SSmov ss, axmov sp, offset tos ;设置spmov ax, data;设置数据段mov ds, axmov bx, offset ary ;bx=数组ary首地址push bx ;压入堆栈sp-=2mov bx, offset count ;count为数组长度的地址push bx;压入堆栈sp-=2mov bx, offset sum;sum为总和的地址push bx;压入堆栈sp-=2call far ptr proadd;CS:IP压入堆栈sp-=4mov ax, 4c00h ;返回DOSint 21hmain endpcode1 endscode2 segmentassume cs:code2proadd proc farpush bp ;保护之前的bp, sp-=2mov bp, sp ;BP为堆栈栈底, BP=SPpush ax ;ax压入堆栈sp-=2push cx ;cx压入堆栈sp-=2push si ;si压入堆栈sp-=2push di ;si压入堆栈sp-=2mov si, [bp+0ah] ;指向arraymov di, [bp+8] ;指向countmov cx, [di] mov di, [bp+6] ;指向sumxor ax, axnext:add ax, [si]add si, 2loop nextmov [di],axpop dipop sipop cxpop axpop bpret 6proadd endpcode2 endsend start

如上图所示,在用栈调用过程时,我们需要在压入的CS、IP后压入BP,通过BP来寻址过程的参数,因此BP是非常重要的。

例: 将下列程序以汇编语言的形式表达:

int g = 5;void main(){int n;n = 3;fun(g, n);}void fun(int i, int j){int x, y;x = i;y = j;}

data segmentgdw5data endscode segmentmain proc farassumecs:code, ds:datastart:push bp ;BP压入堆栈mov bp, sp ;BP指向SPpush ax ;AX压入堆栈mov ax, data ;设置数据段mov ds, axsub sp, 2 ;SP指向SP-2处mov[bp-4], 3 ;存储局部变量n=3push g ;g压入堆栈push [bp-4] ;压入局部变量n=3 ;这两句是传递的参数call fun ;压入CS:IP到堆栈add sp, 2 ;main函数中局部变量n作废pop axpop bpmov ax, 4c00hint 21hmain endpfun proc farpush bp ;BP压入堆栈mov bp, sp ;BP指向SPpush ax ;AX压入堆栈sub sp, 4 ;4个字节,存储两个变量x,ymov ax, [bp+8] mov [bp-4], ax ;x=imov ax, [bp+6] mov [bp-6], ax ;y=jadd sp, 4 pop ax ;弹出AXpop bp ;弹出BPret 4 ;先弹出ip,cs; main调用fun之前压入了4个字节的参数;作废堆栈栈顶4个字节的变量fun endpcode endsend start

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。