文章目录

  • 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. 段定义语句

段定义语句 SEGMENTENDS ,用来定义一个逻辑段
例 用段定义语句定义一个数据段, 段名为 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. 过程定义语句

结构和功能相同,仅有一些变量赋予的值不同的程序段独立编写,用过程定义伪指令 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个字单元,初值均为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       ;整个源代码结束

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

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

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

设置堆栈后,紧接着用下面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 ,否则其它程序将无法运行,还会导致死机。返回 DOS3 种方法:
1)按程序框架设定的方法返回。先将主程序定义为一个远过程,再执行 3 条指令:

PUSH DS
SUB  AX, AX
PUSH AX
...
RET

DS00H 推入栈,再执行 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、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等待从键盘输入一个字符

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

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

例 交互式程序中,用户键入字母键 YN ,分别转入不同的程序去处理,并在 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 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      ;功能号0
INT 16H       ;等待键盘输入

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

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

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

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码,包括全部控制符以及积分符、希腊字母等。

例 由人机对话从键盘输入 110 进制数(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 过程调用 ★★★

例 内存中有两个数组 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 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 汇编语言程序设计相关推荐

  1. 【微机原理与接口技术学习实践】汇编语言程序设计初步——debug编写调试指令序列

    halo~我是bay_Tong桐小白 本文内容是桐小白个人对所学知识进行的总结和分享,知识点会不定期进行编辑更新和完善,了解最近更新内容可参看更新日志,欢迎各位大神留言.指点 [微机原理与接口技术学习 ...

  2. 微机原理与接口技术 学习笔记(五) 8255(可编程并行接口芯片) 与 8253(可编程定时/计数器)

    文章目录 一,可编程并行接口芯片8255A 1. 8255A的结构 1.1 数据端口 1.2 端口控制逻辑 1.3 数据总线缓冲器 1.4 读/写控制逻辑 2. 方式选择 2.1 方式选择控制字 2. ...

  3. 微机原理与接口技术复习笔记(1)——微型计算机概述

    作者:BerenCamlost 目录 作者:BerenCamlost 第一章 微型计算机概述 1.1 ASCII码 1.2 微机的基本组成 1.3 总线 1.4 微机的工作过程 第一章 微型计算机概述 ...

  4. 微机原理与接口技术系列笔记(一)

    第一章 微型计算机概述 提起计算机的发展不得不提到以下两人,一位是计算机科学之父.人工智能之父--图灵,提出了图灵机模型和图灵测试,其中图灵测试给出了人工智能的概念. 另一位是冯·诺伊曼,他提出了计算 ...

  5. 《微机原理与接口技术》期末复习笔记

    微机原理与接口技术 第01章 微机原理概述 基本知识 CPU:中央处理单元(Central Processor Unit),处理器,能够分析和执行指令的部件,能分析和执行指令的芯片就是CPU MPU: ...

  6. 微机原理与接口技术--西安电子科技大学-笔记一

    微机原理与接口技术-西安电子科技大学 绪论 微型计算机系统组成 硬件子系统与软件 早期计算机硬件组成–ALU-----控制器控制ALU运算–ALU运算的数据 存储器中的数据被控制器控制器送到ALU 输 ...

  7. 微机原理与接口技术(一)

    导论 本书讲的是,微型计算机基本原理,和微机接口控制. 能学习到 微机基本原理(内容:微机结构,指令集): 输入输出接口控制以及软硬件设计(硬件设计:存储器(内存)及接口,输入输出技术软件设计:汇编语 ...

  8. 微型计算机系统中的内部寄存,微机原理与接口技术习题集汇总.doc

    微机原理与接口技术习题集汇总.doc (50页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 29.9 积分 .word格式,第二章 微机基本组成及工作原 ...

  9. 微型计算机原理设计存储系统,微机原理与接口技术存储器设计.pdf

    2018版 微机原理与接口技术 第六章 存储器设计 董明皓 dminghao@xidian.edu.cn 准备知识 存储器的性能指标-存储容量(常用单位) 存储容量的表示  Bit -- 用二进 ...

  10. 哈尔滨工程大学微型计算机原理与接口技术,哈尔滨工程大学微机原理与接口技术第2-3讲.ppt...

    哈尔滨工程大学微机原理与接口技术第2-3讲.ppt (28页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.90 积分 第1章 计算机接口基本知识1. ...

最新文章

  1. h5 和native 交互那些事儿
  2. Nginx面试三连问:Nginx如何工作?负载均衡策略有哪些?如何限流?
  3. mysql事务的4大特性
  4. 判断一件事有无技术含量的标准
  5. 【ZJOI2015】幻想乡战略游戏【点分树】【带权重心】
  6. php处理上传文件的步骤,php文件上传步骤
  7. 不解禁administrator账号的情况下以管理员身份运行bat文件
  8. 一步一步手绘Spring IOC运行时序图三(基于Annotation的IOC容器初始化)
  9. java 导出表格打包zip文件下载_asyExcel导出excel并打包成zip压缩包下载
  10. 引用服务器js文件写法,Vue 公共js文件如何放在服务器上引用
  11. Javascript模式——函数提升 (笔记)
  12. Atitit Seed-Filling种子填充算法attilax总结
  13. innodb 删除隐藏列_MySQL进阶之InnoDB事务原子性实现原理
  14. android日记功能的实现6,我的android studio学习日记
  15. 英文数字字母听力模拟的简单实现
  16. 高级PPT动画制作示例
  17. 【codevs 1329】东风谷早苗
  18. outlook服务器邮件满了怎么办,outlook邮箱满了怎么清理_outlook一直提示邮箱满了如何清理-win7之家...
  19. mysql ibd文件清理_MYSQL .ibd文件数据恢复
  20. [Halcon例程学习]增强指纹纹理的coherence_enhancing_diff

热门文章

  1. html在ie浏览器中中文为什么是乱码
  2. Ubuntu Server上如何安装Gi
  3. Hexo博客使用腾讯云CDN加速及优化
  4. 面向对象-抽象类与接口
  5. 华为交换机SEP双半环设计方案及配置详细步骤
  6. lumen php命令,lumen添加artisan 命令方法
  7. GPU、GPU驱动、OpenGL、游戏引擎之间的关系
  8. Building Worlds In Unreal 学习笔记——07-11 岩石树落木灌木绘制/溪水着色器/潮湿与焦散贴花/后处理
  9. iOS开发特殊日期灰色界面的实现
  10. 社科院与杜兰大学金融管理硕士项目——与优秀的人同行,做更好的自己!