【微机原理与接口技术】学习笔记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 16H
- 1)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. 段定义语句
段定义语句 SEGMENT
和 ENDS
,用来定义一个逻辑段。
例 用段定义语句定义一个数据段, 段名为 DATA
, 段中包含 X、Y
两个变量。
DATA SEGMENT ;数据段开始,DATA为段名;表示该段的基址
X DW 1234H ;变量X的段基址:偏移量;=DATA:0000 内容为1234H
Y DB 56H ;变量Y的段基址: 偏移量;=DATA:0002 内容为56H
DATA ENDS ;数据段结束
段定义语句的一般形式:
段名 SEGMENT [定位类型] [组合类型] ['分类名']PAGE(页) *NONE 'STACK'*PARA(节) PUBLIC 'CODE'WORD(字) STACK BYTE(字节) COMMONATMEMORY;段中内容
段名 ENDS
加 [ ]
项可省略,但堆栈段的组合类型是 STACK
,不可省略。省略项不写时,其值用带 *
的项,它们是隐含用法,用的是默认值。
2. 段分配语句
段分配语句 ASSUME
告诉汇编程序,4
个段寄存器 CS、DS、SS、ES
分别与哪些段有关。格式如下,也可分两行书写。
ASSUME CS:代码段名, DS:数据段名, SS:堆栈段名, ES:附加段名
3. 过程定义语句
将结构和功能相同,仅有一些变量赋予的值不同的程序段独立编写,用过程定义伪指令 PROC
和 ENDP
进行定义,并把这些程序段称为过程(Procedure
)或子程序,由主程序中的 CALL
语句来调用它们。
过程定义格式:
过程名 PROC [NEAR]/FAR... ;过程内容RET
过程名 ENDP
在 PROC
伪语句中,必须说明是近过程 NEAR
还是远过程 FAR
,NEAR
可以省略不写。在过程内部必须安排一条返回指令 RET
或 RET n
,以便返回主程序。
过程像标号一样,有 3
种属性: 段基址、偏移地址和距离属性( NEAR
或 FAR
),它可作为 CALL
指令的操作数。
用CALL语句调用过程,无需说明是近调用还是远调用。例如:
CALL 过程名
4. 变量定义语句
变量定义语句的一般形式为:
变量名 伪指令指示符 操作数 ;注释
说明如下:
- 变量名用符号表示,也可以省略。
- 伪指令包括
DB、DW、DD、DQ
和DT
,分别定义字节、字、双字、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个字单元,初值均为0
N3 DB 100 DUP (3 DUP(8), 6) ;定义100个"8, 8, 8, 6"的数据项
数据项也可写成单个字符或字符串的形式,通常用字节来表示。 如 DB 'Welcome'
在内存中顺序存放各字符的ACSII码。
例 如数据在存储单元中的存放形式如图,试给出相应的变量定义语句。
DATA1 DB '3', 'A'
DATA2 DW 98, 100H, -2
DATA3 DD 12345678H
DATA4 DB 100 DUP (0)
5. 程序结束语句
程序结束语句的格式为:END [标号名或名字]
。它位于程序的最后一行,指示源程序结束,遇到 END
伪指令则停止汇编。标号名或名字可省略。
6. 其它伪指令 (略)
4.1.3 完整的汇编语言程序框架
完整的汇编语言程序包含数据段、代码段、堆栈段和附加数据段。其中:
- 代码段是必须要有的;
- 堆栈段根据情况设置;
- 代码段中要用到变量或数据时,应该设置数据段;
- 当代码段中有字符串操作指令时,不仅要设置数据,还必需设置附加段,而且必须将源串存放在数据段中,而把目的串放在附加段中。
下面先给出程序框架,再介绍如何设置堆栈段,以及程序结束后怎样返回DOS操作系统。
1. 完整的汇编语言程序框架
例 汇编语言程序框架。
DATA SEGMENT ;数据段X DB ?Y DW ?
DATA ENDS
;
EXTRA SEGMENT ;附加段ALPHA DB ?BETA DW ?
EXTRA ENDS
;
STACK SEGMENT PARA STACK 'STACK' ;堆栈段STACK绝不可少STAPN DB 100 DUP(?) ;定义100字节空间TOP LABEL WORD ;TOP为字类型,偏址100,第100个字节处(TOP不代表任何变量,是栈底)
STACK ENDS;代码段
CODE SEGMENT
MAIN PROC FAR ;过程定义语句 ASSUME CS:CODE, DS:DATA, ES:EXTRA, SS:STACK ;说明4个段寄存器分别与哪些段有关
START: MOV AX, STACK ;设堆栈段寄存器SS:SPMOV SS, AX MOV 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 ;返回DOS
MAIN ENDP ;MAIN过程结束
CODE ENDS ;代码段结束END START ;整个源代码结束
代码段、数据段、附加段和堆栈段,都用段定义伪指令 SEGMENT
和 ENDS
定义。数据段或附加段,用DB、DW
等伪指令设置实际数值。堆栈段定义了100字节空间,其数值也可修改。
代码段用来存放可执行的指令序列。这里用 PROC FAR
和 ENDP
伪指令将整个程序编写成一个远过程的形式,过程名为 MAIN
。最后一条指令语句为过程返回指令 RET
,使程序执行完后返回到调用它的地方。
MAIN
过程中,首先用段分配伪指令 ASSUME
告诉汇编程序,4
个段寄存器分别与哪些段相对应,但不能将段基地址装入相应的段寄存器中,还要给 DS
、ES
和 SS
寄存器赋初值,CS则由操作系统赋初值。对于堆栈段,要给 SS
和 SP
赋初值,以设定堆栈。
设置堆栈后,紧接着用下面3条指令,将DS推入堆栈保护起来,再使00H入栈,以便在程序结束时,能执行RET
指令来返回DOS,即:
PUSH DS ;DS入栈
SUB AX, AX
PUSH AX ;00H入栈
用户编写的程序的具体内容,放在初始化程序之后,RET
指令之前。代码段之后,再安排一条 END START
指令,汇编程序遇到这条指令后就结束汇编,并自动从 START
开始往下执行程序。
2. 堆栈的设置
如果程序中没有定义堆栈段,连接时会给出一个警告信息:Warning: no stack segment
。此错误不影响连接过程的完成,这时,DOS会自动定义一个堆栈段,使程序仍可正常运行。
3. 返回DOS操作系统
汇编语言程序在 DOS
下运行结束后,应能正确返回 DOS
,否则其它程序将无法运行,还会导致死机。返回 DOS
的 3
种方法:
1)按程序框架设定的方法返回。先将主程序定义为一个远过程,再执行 3
条指令:
PUSH DS
SUB AX, AX
PUSH AX
...
RET
将 DS
和 00H
推入栈,再执行 RET
指令,转去执行 INT 20H
指令,返回DOS。这是返回DOS的常规方法。
2)执行 4CH
号DOS功能调用。程序结束前按如下方法使用 4CH
号DOS功能调用指令,返回DOS。
MOV AX, 4C00H ;AH=4CH, 是DOS功能号;AL通常置为0
INT 21H
这种方法功能更强,更安全,使用也比较方便,建议使用这种方法返回 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、33H
为BIOS中断,即保存在系统ROM BIOS中的BIOS功能调用。n=20H~2EH
为DOS中断,应用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
,等待从键盘输入一个字符。
MOV AH, 01H ;AH功能调用号01H
INT 21H ;AL读入键值, 并显示该字符
若有键压下,读入键值, 并检查是否为 Ctrl-Break
键?若是,自动调用 INT 23H
中断, 执行退出命令;否则将键值送入 AL
,并显示该字符。
例 交互式程序中,用户键入字母键 Y
或 N
,分别转入不同的程序去处理,并在 CRT
上显示键入字符; 若按了 Ctrl-Break
,则结束程序,否则继续等待。
GET_KEY:MOV AH, 01H ;AH功能调用号01HINT 21H ;AL读入键值CMP AL, '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 21H
的 5
号DOS功能调用,也可用 BIOS
的 INT 17H
的 0
号中断调用。
由于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 ;功能号0
INT 16H ;等待键盘输入
键盘上的键用 2
个 8
位数值进行标记:
- 最高位
b7
决定该键是压下还是松开,b7=0
,表示该键压下,b7=1
,表示键已松开。 - 后
7
位是这样定义的: 对于有ASCII码的键来说,第一字节为ASCII码,第二字节为键盘扫描码,后者由系统根据键的位置确定; - 对于无ASCII码的键来说,第一字节为
0
,第二字节为扩展码。
这样,利用 INT 16H
的 0
号功能调用,就可知道是哪个键压下了或松开了。
2)1号功能调用
查询键盘缓冲区,对键盘扫描,但不等待。
例 编程查看键盘缓冲区。
MOV AH, 1 ;功能号1
INT 16H
调用结果:
ZF=0
,键盘缓冲区不空,有键按了,AL
=键入字符ASCII码,AH
=扫描码;ZF=1
,缓冲区空。
4.3 汇编语言程序设计方法与实例
4.3.1 顺序结构程序设计
如果用循环程序将 00~FFH
先后送入 DL
,再利用DOS的 2
号功能调用,则可显示全部的标准和扩展ASCII码,包括全部控制符以及积分符、希腊字母等。
例 由人机对话从键盘输入 1
个 10
进制数(0~9
),查表求键入数字的平方值,存入 AL
寄存器中,并显示有关的提示信息。试编写汇编语言程序。
解:
数据段中,先给出数字 0~9
的平方值,逐个存入 TABLE
开始的内存中,形成表格,以便查找,再给出等待显示的提示信息。
代码段由 3
个部分组成: 显示提示信息;等待键入数字;查表求键入数字的平方值,并将结果存入AL
中。
程序如下:
DATA SEGMENTTABLE DB 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 ;数字0~9的平方值BUF DB 'Please input a number(0~9):', 0DH, 0AH, '$' ;提示信息
DATA ENDS
CODE SEGMENTASSUME CS:CODE, DS: DATA
START:MOV AX, DATAMOV DS, AX ;设置DSMOV DX, OFFSET BUF ;设置DX,使字符串首地址=DS:DXMOV AH, 9H ;9号DOS功能调用INT 21H ;显示提示信息MOV AH, 01H ;1号功能调用,等待键入字符INT 21H ;AL=键入数字的ASCII码AND AL, 0FH ;AL=截下数字值;(表内元素序号)MOV BX, OFFSET TABLE ;BX指向表头地址TABLEMOV AH, 0 ;AX寄存器高字节清0ADD BX, AX ;表头地址+键入数字(AL),结果存入BXMOV AL, [BX] ;查表求得平方值MOV AX, 4C00HINT 21H ;返回DOS
CODE ENDSEND START
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
个数,直至所有字符处理完后,将统计出的个数送入相应存储单元。
DATA SEGMENTBUF DB '+36', 'PRINT', 'abc', '2A0CH', '#' ;一串字符COUNT EQU $-BUF ;$=当前地址, COUNT=字符总个数NUM DB 3 DUP(?) ;先后存放存数字、字母和其它字符个数
DATA ENDSCODE SEGMENTASSUME CS:CODE, DS:DATA
START: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 NEXT
ABC: CMP AH, 41H ;<41H?JL NEXT ;③是,非字母,转CMP AH, 5AH ;>5AH?JG NEXT ;⑤是,非字母,转INC DL ;④否,字母个数增1
NEXT: INC BX ;基地址指针加1DEC CH ;字符串长度减1JNZ LOOP1 ;未完,取下一个数
4.3.3 循环结构程序
例 在一串给定个数的数据中寻找最大值,存放到 MAX
存储单元中。
DATA SEGMENTBUF DW 1234H, 3200H, 4832H, 5600H ;一串字数据COUNT EQU ($-BUF)/2 ;数据个数(循环次数)MAX DW ? ;存最大值
DATA ENDS
;
STACK SEGMENT 'STACK'STAPN DB 100 DUP(?)TOP EQU LENGTH STAPN
STACK ENDS
;
CODE SEGMENT
MAIN PROC FARASSUME CS:CODE, SS:STACK
START: 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 ;循环次数减1
AGAIN: INC BX ;修改地址指针INC BXCMP AX, [BX] ;AX与后取的数比较JGE NEXT ;如AX中数大于等于后者,则转MOV AX, [BX] ;如后取的数大,则将其送AX
NEXT: LOOP AGAIN ;没处理完,转(循环操作)MOV MAX, AXRET ;返回DOS
MAIN ENDP ;处理完,结束
CODE ENDSEND MAIN
本例通过 LOOP
指令执行循环操作,取字符串的地址指针 BX
要用指令修正,以指向下个字单元取数进行比较。
例 有一个无符号数组共含5个元素:12, 7, 19, 8, 24
, 它们存放在 LIST
开始的字单元中,编程将数组中的数按从大到小的次序排列(元素个数 n=5
)。
编程思路:
- 编程时采用冒泡法排序。
- 比较从第1个元素开始,与相邻数比较,若大的在前小的在后,次序就排好了,不要交换,否则交换。
- 然后将小的数与第3个元素比较,经
n-1(=4)
次比较后,一行中最小的元素7
排到了最后面。共循环比较了n-1(=4)
次。 - 再作第二轮比较,这轮只要比较
n-2(=3)
次,即可将数组中的数按从大到小的次序排列好。
这是一个多重循环程序。程序稍加修改后,即可实现从小到大的排序。
DATA SEGMENTLIST DW 12,7,19,8,24 ;数组字单元COUNT EQU ($-LIST)/2 ;数组长度n=10/2=5
DATA ENDS
;
SORT SEGMENTASSUME CS: SORT, DS: DATA
BEGIN: 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 21H
SORT ENDSEND BEGIN
例 折半查找算法:
data segmentarray dw 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, 1
compare:cmp ax, [bx+si] ;要查找的数55和arr[mid]进行比较je exit ;相等退出ja higher ;大于dec cxmov high_idx, cx jmp mid ;进行下一轮折半
higher:inc cxmov low_idx, cxjmp mid
no_match:stc
exit:...
4.3.4 代码转换程序(略)
4.3.5 过程调用 ★★★
例 内存中有两个数组 ARY1
和 ARY2
,数组长度为 20
和 10
,要求编写一个程序,分别累加两个数组的值,存入 SUM1
和SUM2
开始的单元中,低字节在前,高字节在后。
累加第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 WORD
STACK ENDSCODE SEGMENT ;代码段
MAIN PROC FAR ;主程序ASSUME CS:CODE, DS:DATA, SS:STACK
BEGIN: MOV AX, STACK ;设置堆栈段SS:SPMOV SS, AX MOV SP, OFFSET TOPPUSH DSSUB AX, AXPUSH AXMOV AX, DATA ;设置数据段MOV DS, AX;对第一个数组求和LEA SI, ARY1 ;转子程序前入口参数, SI=ARY1首地址MOV CX, LENGTH ARY1 ;CX=ARY1长度MOV BX, OFFSET SUM1 ;BX=和单元首址CALL SUM ;转子过程,求数组1之和;对第二个数组求和LEA SI, ARY2 ;转子程序前设ARY2之入口参数MOV CX, LENGTH ARY2 MOV BX, OFFSET SUM2 CALL SUM ;转子过程,求数组2之和RET ;返回DOS
MAIN ENDP ;主程序结束
;
;子程序SUM
;
SUM PROC NEAR ;求和子过程SUMXOR AL, AL ;AX清0,CF标志清0MOV AH, 0 ;AH存进位,初值清0
LOOP1: ADC AL, [SI] ;数组中取一元素,;带进位累加到ALADC AH, 0 ;进位累加到AH中INC SI ;修改地址指针LOOP LOOP1 ;未完,继续MOV [BX], AL ;已处理完,存和数, 低地址字节在前MOV [BX+1], AH ;存进位累加值; 高地址字节在后RET
SUM ENDP ;SUM子过程结束
;
CODE ENDSEND MAIN ;整个程序结束
从上面的示例中,我们看到,程序通过寄存器来向子过程中传递参数,做法没问题,但是不能代表典型的做法——比如说,子过程的参数太多以至于寄存器不够用;传递的参数是一个结构体等等……我们需要知道一个通用的规则,用于向子过程传递多种类型的、数量不定的参数——现实的做法,是通过堆栈传送参数地址。
例 累加数组中的元素:
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 segment
main proc farassume cs:code1, ds:data, ss:stack
start: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 21h
main endp
code1 ends code2 segmentassume cs:code2
proadd 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, ax
next:add ax, [si]add si, 2loop nextmov [di],axpop dipop sipop cxpop axpop bpret 6
proadd endp
code2 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 segmentg dw 5
data endscode segment
main proc farassume cs:code, ds:data
start: 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 21h
main endpfun proc far push 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 endp
code endsend start
【微机原理与接口技术】学习笔记4 汇编语言程序设计相关推荐
- 【微机原理与接口技术学习实践】汇编语言程序设计初步——debug编写调试指令序列
halo~我是bay_Tong桐小白 本文内容是桐小白个人对所学知识进行的总结和分享,知识点会不定期进行编辑更新和完善,了解最近更新内容可参看更新日志,欢迎各位大神留言.指点 [微机原理与接口技术学习 ...
- 微机原理与接口技术 学习笔记(五) 8255(可编程并行接口芯片) 与 8253(可编程定时/计数器)
文章目录 一,可编程并行接口芯片8255A 1. 8255A的结构 1.1 数据端口 1.2 端口控制逻辑 1.3 数据总线缓冲器 1.4 读/写控制逻辑 2. 方式选择 2.1 方式选择控制字 2. ...
- 微机原理与接口技术复习笔记(1)——微型计算机概述
作者:BerenCamlost 目录 作者:BerenCamlost 第一章 微型计算机概述 1.1 ASCII码 1.2 微机的基本组成 1.3 总线 1.4 微机的工作过程 第一章 微型计算机概述 ...
- 微机原理与接口技术系列笔记(一)
第一章 微型计算机概述 提起计算机的发展不得不提到以下两人,一位是计算机科学之父.人工智能之父--图灵,提出了图灵机模型和图灵测试,其中图灵测试给出了人工智能的概念. 另一位是冯·诺伊曼,他提出了计算 ...
- 《微机原理与接口技术》期末复习笔记
微机原理与接口技术 第01章 微机原理概述 基本知识 CPU:中央处理单元(Central Processor Unit),处理器,能够分析和执行指令的部件,能分析和执行指令的芯片就是CPU MPU: ...
- 微机原理与接口技术--西安电子科技大学-笔记一
微机原理与接口技术-西安电子科技大学 绪论 微型计算机系统组成 硬件子系统与软件 早期计算机硬件组成–ALU-----控制器控制ALU运算–ALU运算的数据 存储器中的数据被控制器控制器送到ALU 输 ...
- 微机原理与接口技术(一)
导论 本书讲的是,微型计算机基本原理,和微机接口控制. 能学习到 微机基本原理(内容:微机结构,指令集): 输入输出接口控制以及软硬件设计(硬件设计:存储器(内存)及接口,输入输出技术软件设计:汇编语 ...
- 微型计算机系统中的内部寄存,微机原理与接口技术习题集汇总.doc
微机原理与接口技术习题集汇总.doc (50页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 29.9 积分 .word格式,第二章 微机基本组成及工作原 ...
- 微型计算机原理设计存储系统,微机原理与接口技术存储器设计.pdf
2018版 微机原理与接口技术 第六章 存储器设计 董明皓 dminghao@xidian.edu.cn 准备知识 存储器的性能指标-存储容量(常用单位) 存储容量的表示 Bit -- 用二进 ...
- 哈尔滨工程大学微型计算机原理与接口技术,哈尔滨工程大学微机原理与接口技术第2-3讲.ppt...
哈尔滨工程大学微机原理与接口技术第2-3讲.ppt (28页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.90 积分 第1章 计算机接口基本知识1. ...
最新文章
- h5 和native 交互那些事儿
- Nginx面试三连问:Nginx如何工作?负载均衡策略有哪些?如何限流?
- mysql事务的4大特性
- 判断一件事有无技术含量的标准
- 【ZJOI2015】幻想乡战略游戏【点分树】【带权重心】
- php处理上传文件的步骤,php文件上传步骤
- 不解禁administrator账号的情况下以管理员身份运行bat文件
- 一步一步手绘Spring IOC运行时序图三(基于Annotation的IOC容器初始化)
- java 导出表格打包zip文件下载_asyExcel导出excel并打包成zip压缩包下载
- 引用服务器js文件写法,Vue 公共js文件如何放在服务器上引用
- Javascript模式——函数提升 (笔记)
- Atitit Seed-Filling种子填充算法attilax总结
- innodb 删除隐藏列_MySQL进阶之InnoDB事务原子性实现原理
- android日记功能的实现6,我的android studio学习日记
- 英文数字字母听力模拟的简单实现
- 高级PPT动画制作示例
- 【codevs 1329】东风谷早苗
- outlook服务器邮件满了怎么办,outlook邮箱满了怎么清理_outlook一直提示邮箱满了如何清理-win7之家...
- mysql ibd文件清理_MYSQL .ibd文件数据恢复
- [Halcon例程学习]增强指纹纹理的coherence_enhancing_diff