第 4章 汇编语言程序设计
4,1 汇编语言的基本语法
4,2 汇编语言程序设计
4,1 汇编语言的基本语法
汇编语言源程序的格式
常量、标识符和表达式
指示性语句
名字与变量
指令性语句
4.1.1,汇编语言源程序的格式
DATA SEGMENT AT 2000H
ARRAY DB a1,a2,a3,… a10( a1~ a10为立即数 )
COUNT EQU $ - ARRAY
SUM DW?
DATA ENDS
STACK SEGMENT PARA STACK ‘STACK’
STAK DB 10 DUP (?)
TOP EQU LENGTH STAK
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
START,MOV AX,DATA
MOV DS,AX
MOV AX,0
MOV DI,OFFSET SUM
MOV BX,OFFSET ARRAY
MOV CX,COUNT
LOP,ADD AL,[BX]
ADC AH,0
INC BX
LOOP LOP
MOV [DI],AX
MOV AH,4CH
INT 21H
CODE ENDS
END START
汇编语言源程序格式的特点是,
1,分段结构
2,语句行汇编语言程序的语句有两类:
( 1 ) 指令性语句
( 2 ) 指示性语句
( 1 ) 指令性语句格式为:
[ 标号 ],操作码 [ 操作数 ] [; 注释 ]
带方括号的为任选项,
标号是指令的符号地址,
操作码和操作数是用汇编语言指令表示的指令部分,
注释是对该语句在本程序中的功能的说明。
( 2 ) 指示性语句格式为:
[ 名字 / 变量 ] 命令 [参数 ] [; 注释 ]
命令指示汇编程序进行某种汇编操作,
参数是有关的数据,
名字是段名、符号名等标识符,变量用变量名表示。
4,1,2,常量、标识符和表达式一、常量其值在程序中不能改变的量,
在汇编语言源程序中的常量有:
数字常量、字符常量和符号常量
( 1 ) 数字常量可以是二进制数、八进制数、十进制数或十六进制数表示
( 2 ) 字符常量是用单引号‘ ’括起来的 ASCII字符,
其值是该字符的 ASCII代码值
( 3 ) 符号常量是用标识符定义的常量。
例如,采用符号定义语句 PORTA EQU 80H,
则指令 MOV AL,PORTA
与指令 MOV AL,80H 等价。
二、标识符在编程时建立的有特定意义的字符序列,标识符可用作符号常量、名字、变量和标号等。
组成标识符有如下规定:
( 1 ) 组成标识符的字符有:
英文大写字母,A~Z;
英文小写字母,a-z;
数字 0~9;
字符?,@,_,$;
( 2 )字符长度不超过 3l个;
( 3 ) 首字符不能是数字;
( 4 )? 不能单独作为一个标识符;
( 5 ) 不能把保留字用作标识符,保留字包括助记符、
寄存器名等。
三、表达式表达式由操作数和运算符组成。
操作数可以是常量、名字、变量和标号等。
运算符包括算术运算符逻辑运算符关系运算符分析运算符合成运算符等。
表达式中的运算符高优先级低
1 括号中的项,即( … )和 […]
2 LENGTH,SIZE,WIDTH,MASK
3 PTR,OFFSET,SEG,TYPE,THIS
4 ×,/,MOD
5 +,-
6 EQ,NE,L,LE,GT,GE
7 NOT
8 AND
9 OR,XOR
4.1.3 指示性语句一,程序开始和结束语句
( 1 ) NAME 命令给程序模块命名格式,NAME 名字
( 2 ) TITLE 命令格式,TITLE 名字名字作为每页标题打印出来,标题名字符不大于 60个。
( 3 ) END命令格式,END [ 标号 ]
主模块结束必须写上标号,
标号是程序中第一句指令性语句的标号。
二、段定义语句用来定义一个段
( 1 ) SEGMENT / ENDS命令格式,段名 SEGMENT [参数 ]
…….
段名 ENDS
段名由程序员定义,汇编时由系统为该段分配一个段基值,并将该值赋于段名。
SEGMENT语句中的参数为:
[ 定位类型 ] [ 组合类型 ] [ ‘ 类别 ’ ]
用来设定该段在内存中的位置和在汇编、连接时,当该段与其它段组合在一起时该段同其它段的连接关系。都是任选项
( 2 ) ORG命令偏移地址定位语句指定某一语句在段内的偏移地址格式,ORG exp
exp 为能计算出 16位立即数的表达式例:
ORG 2000H
ORG $ + 1000H
( 3 ) ASSUME 命令
ASSUME 命令紧跟在代码段的 SEGMENT语句之后,用于指示下述程序中段寄存器同指定段名的对应关系格式,
ASSUME CS:段名 1,DS:段名 2,
SS,段名 3,ES:段名 4
注意,ASSUME语句仅指出段寄存器同段名的对应关系,并未对段寄存器赋值,在程序中未用到的段寄存器不必指示。
三、过程定义语句应用 PROC /ENDP命令定义过程 ( procedure ),
过程即子程序,是程序的一部分,程序中用 CALL
指令调用过程,过程结束用 RET指令返回程序。
格式,过程名 PROC 类型
……
过程名 ENDP
类型为 FAR / [NEAR]
四、数据定义语句用来为数据分配内存单元。
格式,
[ 变量 ] 命令 参数 1,参数 2,…… [ ;注释 ]
变量是内存中一组数的名字,由程序员在编程时用标识符定义,变量可以作为内存操作数来使用。
数据定义语句中的命令可以是:
DB 定义字节数据 (8 位 )
DW 定义字数据 (16 位 )
DD 定义双字数据 (32 位 )
DQ 定义 8字节数据 (64 位 )
DT 定义 10字节数据 (80 位 )
参数是相应内存单元中的数据,
可以是:
数字常量 …… 用各种规定的数制表示;
字符常量 …… 用单引号括起来的 ASCII字符;
符号常量 …… 用 EQU语句定义。
保留一个存储单位,以备程序执行过程中存入结果数据;
保留 4个存储单位;
ARY DB 4 DUP ( 43H )
表示定义一个变量 ARY,在内存中开辟一个数组,存放 4个 43H。
DUP 为复制符格式,复制次数 DUP (数据 )
例:
DATA1 SEGMENT AT 2000H
ORG 3000H
ARY1 DB 12H,34H,56H,78H
ARY2 DW 1234H,5678H
ARY3 DB ‘A’,’ B’,’ C’,’ D’,,’ABCD’
ARY4 DW ‘AB’
ARY5 DD ‘AB’
ARY6 DW ARY2
ARY7 DD ARY2
在定义 ARY6的语句中,
参数为已定义的变量 ARY2,
其值为 ARY2的偏移地址 3004H;
在定义 ARY7的语句中,
参数为已定义的变量 ARY2,
其值为 ARY2的偏移地址 3004H和段基值 2000H。
五、符号定义语句
( 1 ) EQU 命令 等值命令格式:名字 /变量 EQU exp
( 2) = 命令 等号命令格式,名字 = exp
( 3 ) PURGE命令用来撤消已定义的符号常量格式,PUREG 名字可同时撤消几个己定义的名字
4.1.4 名字与变量一,名字名字包括文件名、标题名、段名、过程名和符号常量名等,是程序员在编程中按标识符规定命名的,
目的是方便编程和读程。
( 1 ) 段名在段定义语句中命名取定,定义后的段名可以作为段基值来使用,用来对段寄存器赋值,例如:
MOV AX,DATA1
MOV DS,AX
( 2 ) 过程名在过程定义语句中定义,指向过程中第一条指令所在的内存单元的地址,即过程的入口地址,有 NEAR和
FAR之分。
( 3 ) 符号常量名由符号定义语句 EQU,=来定义,对符号常量给以赋值。
二,变量
( 1) 变量的定义和属性变量由数据定义语句 DB/DW/DD/DQ/DT来定义。
变量有三属性:
段属性 …… 说明该变量在哪个存储段中;
偏移地址属性 …… 说明该变量的段内偏移量;
类型属性 …… 说明该变量所在内存数据的类型(字节、字、双字,8字节还是 10字节)。
( 2) 分析运算符 ( Analytic operator )
用于把变量的三个属性分别取出来作为操作数。
有 SEG,OFFSET.,TYPE,LENGTH 和 SIZE。
SEG 变量名 ------ 取出变量的段基值
OFFSET 变量名 ------ 取出变量的偏移地址
TYPE 变量名 ------ 取出变量的类型值,字节数据为 1、字数据为 2、双字数据为 4,8字节数据为 8、
10 字节数据为 10。
LENGTH 变量名:表示变量所在数组的元素个数。
注意:只有当数据用 DUP定义时,才适用,
否则恒为 1 。
SIZE 变量名:表示变量所在数组的字节数。
注意,同 LENGTH,
SIZE 变量 = LENGTH 变量 × TYPE 变量
( 3 ) 合成运算符
( a) PTR 运算符类型 PTR exp
其中类型是
BYTE,WORD,DWORD ( 变量 )
FAR,NEAR ( 标号 )
exp为表达式,是存储器操作数,
PTR的应用
用于临时改变变量或标号的类型属性。
例如,BUFW DW 1234H,5678H
MOV AX,BUFW
MOV AL,BYTE PTR BUFW
也可用来指定内存操作数的类型。
例如,INC BYTE PTR [ BX ]
INC WORD PTR [BX ]
也可用 EQU 和 PTR定义一个新的变量名例如,BUFW DW 1234H,5678H
BUFB EQU BYTE PTR BUFW
则 MOV AX,BUFW 和
MOV AL,BUFB
都是合法的。
( b ) THIS 类型类似于 PTR 运算符,
THIS与 EQU一起用来定义一个新变量名。
例如,BUFB EQU THIS BYTE
BUFW DW 1234H,5678H
EQU 语句必须紧跟 DW语句,并且 EQU语句在前。
( c ) LABEL 命令格式:变量 /标号 LABEL 类型用来定义语句中的变量 (或标号 )
例如,BUFB LABEL BYTE
BUFW DW 1234H,5678H
LABEL语句必须紧跟 DW语句,并且 LABEL语句在前。
4.1.5 指令性语句格式,
[ 标号,] 操作码 [ 操作数 ] [;注释 ]
一、标号标号是一条指令在内存中的符号地址标号有三属性
( l ) 段属性:表示标号所在段的段基值;
( 2 ) 偏移量属性:表示标号所在位置的偏移地址;
( 3 ) 类型属性:表示标号所在指令是允许段内跳转还是段间跳转。
用 PTR重新指定标号类型设已定义标号 METER为 NEAR
则段内转移用 JMP METER;
段间转移用 JMP FAR PTR METER
用 EQU 和 PTR 定义新标号:
METER,
KILOMT EQU FAR PTR METER
用 EQU 和 THIS 定义新标号:
KILOMT EQU THIS FAR
METER:
用 LABEL 定义新标号:
KILOMT LABEL FAR
METER:
二、操作数操作数可以用段名、符号常量、变量、属性表达式、
过程名和标号等。
例如:
MOV AX,DATA ; DATA是段名
MOV CX,COUNT ; COUNT 是符号常量
MOV BL,BUFFER ; BUFFER 是变量
MOV SI,OFFSET ARRAY ; OFFSET ARRAY是属;性表达式
CALL SBRT1 ; SBRT1 是过程名
JMP DONE ; DONE 是标号
4.2 汇编语言程序设计
顺序程序
分支程序
循环程序
子程序
系统功能调用
4.2 汇编语言程序设计程序 ( PROGRAM )是完成特定功能的一组指令的有序集合。
程序设计的步骤是:
分析课题确定算法画流程图编写程序上机调试、修改
4.2.1 顺序程序指令顺序执行,无分支、无转移、
无循环。
硬件支持是 IP自动加 1,从内存取出一个指令字节,IP自动加 1。
例 1,查表求平方值
DATA SEGMENT
TABLESQ DB 0,1,4,9,16,25,36,49,
DB 64,81,100,121,144,169,196,225
XX DB X ; X为 0~ 15的正整数
YY DB?
DATA ENDS
STACK SEGMENT PARA STACK’STACK’
DB 50 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUMECS,CS,CODE,DS,DATA,SS,STACK
BEGIN,MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET TABLESQ
MOV AH,0
MOV AL,XX
ADD BX,AX
MOV AL,[BX]
MOV YY,AL
MOV AH,4CH
INT 21H
注意:
1,程序结束方法
1) 设计为一个,过程” (PROCEDURE)
2) 以 MOV AH,4CH
INT 21H
即 4CH号功能调用结束。
2,MOV AH,0; AH置 0的必要性。
3,MOV,ADD 指令中 d和 s必需是相同类型。
例 2 非压缩的 BCD码转换为压缩的 BCD码
MOV AX,DAT1 ; AX=0109
MOV CL,4 ; CL=4
SAL AH,CL ; AH=10H
ROL AX,CL ; AX=0091
ROL AL,CL ; AL=19H
MOV BYTE PTR DATA,AL
DAT1 DW 0109H
4.2.2 分支程序
1,分支程序二要素具有判断和转移功能的程序。
(1) 判断:运算结果的状态标志 CF,PF,ZF,SF,OF;
(2) 转移:主要是条件转移指令 Jcc,cc为条件。
单标志位判断 ……JC,JZ,JO,JP,JS,JAE等;
多标志位判断 ……JA,JG,JGE等。
2,采用比较指令和转移指令实现分支
CMP d,s; d- s
Jcc 标号例 3 符号函数处理有一符号函数 Y=f(X)
1; X > 0
Y= 0; X = 0
-1; X < 0
MOV AL,XX
CMP AL,0
JGE BIGR
MOV AL,0FFH
MOV YY,AL ; X<0时,-1送入 YY单元
HLT
BIGR,JE EQUL
MOV AL,1
MOV YY,AL ; X>0时,1送入 YY单元
HLT
EQUL,MOV YY,AL ; X = 0时,0送入 YY单元
HLT
注意,JGE 是用于帶符号数的指令
MOV AL,0FFH ; AL← -1
例 4 数据块传送程序
DATA SEGMENT
STRG DB 1000 DUP (?)
STG1 EQU STRG + 7
STG2 EQU STRG + 25
STRSE EQU 50
DATA ENDS
STACK SEGMENT PARA STACK ‘STACK’
STARN DB 100 DUP (?)
STACK ENDS
COSEG SEGMENT
ASSUME CS,COSEG,DS,DATA,
ES,DATA,SS,STACK
STACK SEGMENT PARA STACK ‘STACK’
STARN DB 100 DUP (?)
STACK ENDS
COSEG SEGMENT
ASSUME CS,COSEG,DS,DATA,
ES,DATA,SS,STACK
MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV CX,STRSE
MOV SI,OFFSET STG1
MOV DI,OFFSET STG2
CLD ;增量方式
PUSH SI
ADD SI,STRSE-1
CMP SI,DI
POP SI
JB OK
STD ;减量方式传送
ADD DI,STRSE-1 ;指向数据块底部
ADD SI,STRSE-1
OK,REP MOVSB ;重复传送 50个数据
MOV AH,4CH
INT 21H
变量为 STRG,STGl,STG2
符号常量为 STRSE
MOVSB 的功能为 [ DS,SI]B→ [ ES,DI] B
DS=ES=DATA
注意 CLD和 STD的作用。
3,利用跳转表实现分支内存中连续存放的一系列跳转地址、
跳转指令或关键字组成一个决定程序分支的跳转表。
主要问题是计算表地址表地址 = 表基地址 + 偏移量例 5 跳转表中存放跳转地址
DATA SEGMENT
BASE DW SBR0,SBR1,SBR2,SBR3
DW SBR4,SBR5,SBR6,SBR7
BN DB X ; X为 0~ 7之间的正整数
DATA ENDS
STACK SEGEMNT PARA STACK ‘STACK’
DB 100 DUP(?)
COSEG SEGMENT
ASSUME CS:COSEG,DS:DATA,
SS:STACK
MOV AX,DATA
MOV DS,AX
MOV AL,BN
MOV AH,0
ADD AL,AL
MOV BX,OFFSET BASE
MOV AH,0
ADD AL,AL
MOV BX,OFFSET BASE
ADD BX,AX
MOV AX,[BX]
JMP AX
MOV AL,4CH
INT 21H
MOV AX,[BX]
JMP AX
可用 JMP WORD PTR [BX] 替代例 6 跳转表中存放跳转指令
MOV AH,0
MOV BL,AL
ADD AL,AL
ADD AL,BL
MOV BX,OFFSET BASE0
ADD BX,AX ;表地址计算
JMP BX
4.2.3 循环程序例 7 计算 Y= ∑ai
DATA SEGMENT
TABLE DW a1,a2,a3,a4,a5,a6,a7,a8,a9,a10
DW a11,a21,……………………..a20
…….
DW a91,a92,…………………… a100
YY DW?
DATA ENDS
程序一
MOV AX,DATA 1
MOV DS,AX 2
MOV AX,0 3
ADD AX,TABL 4
ADD AX,TABL+2 5
…………
ADD AX,TABL+198 103
MOV YY,AX 104
其中 ADD指令有 l00条程序二:
程序一中 4~103条指令可用如下 2 ~ 8条指令替代
MOV AX,0 1
MOV BX,OFFSET TABL 2
MOV CX,100 3
LOP,ADD AX,[BX] 4
INC BX 5
INC BX 6
DEC CX 7
JNZ LOP 8
MOV YY,AX 9
MOV AH,4CH 10
INT 21H 11
程序二为一循环结构程序,其中,
指令 1~ 3为初始化部分:
指令 4为循环工作部分:
指令 5,6为循环修改部分:
指令 7,8为循环控制部分:
指令 9为结束处理部分:
指令 10,11为程序结束部分。
指令 4~8三部分称为循环体,标号 LOP处称为循环头部。
例 8 统计数组中负元素的个数数据块的定义如下:
DATA SEGMENT
D1 DB -1,-3,5,6,-9 ;定义数组
COUNT EQU $ -D1
RS DW? ;存放负数个数
DATA ENDS
代码段程序为:
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
ASSUME SS,STACK
MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET D1 ;建立数据指针
MOV CX,COUNT ;设置计数器初值
MOV DX,0 ;设置结果初值
LOP1,MOV AL,[BX]
CMP AL,0
JGE JUS
INC DX
JUS,INC BX
DEC CX
JNZ LOP1 ;或 LOOP LOP1
MOV RS,DX
MOV AH,4CH
INT 21H
例 7和例 8都是” 先执行,后判断,的结构。
例 9,统计寄存器 AX中,1,的个数。
MOV CX,0
LOP,AND AX,AX
JZ STP
SAL AX,1
JNC LOP
INC CX
JMP LOP
STP,HLT
这是一个“先判断后执行” 的结构。
例 10,软件延时程序
SOFTDELAY PROC
MOV BL,10
DELAY,MOV CX,2801
WAIT,LOOP WAIT
DEC BL
JNZ DELAY
RET
SOFTDELAY ENDP
这是一个二重循环程序,
内循环为,WAIT,LOOP WAIT
每个内循环可实现延时 l0ms;
外循环入口为 DELAY,
共进行 l0次,总延时 100ms。
4.2.4 子程序
1,子程序定义及结构
* 主程序与子程序子程序是一段相对独立的、能实现一定功能的、
能被其它程序调用的程序段。
能调用子程序的程序称为主程序。
* 调用与返回主程序用 CALL指令调用子程序,
子程序用 RET指令返回主程序。
* 子程序文件,子程序说明与子程序单 。
1) 子程序说明
① 功能描述:包括子程序名称,功能以及性能指标 (如执行时间 )等;
② 所用寄存器和存储单元;
③ 子程序的入口,出口参数;
④ 子程序中又调用的其它子程序;
⑤ 调用实例 (可无 )。
子程序说明举例如下:;子程序 DTOB;两位十进制数 (BCD码 )转换成二进制数;入口参数,AL中存放被转换数;出口参数,CL中存放转换后的二进制数;所用寄存器,BX;执行时间,0.06ms
2,子程序应用中应注意的问题
* 主程序与子程序的连接
* 寄存器和工作单元中内容的保护,即保护现场,
用 PUSH指令和 POP指令,可以安排在子程序中,
也可以安排在主程序中。
通常安排在子程序中。
* 参数的传递即主程序如何把入口参数带入子程序,子程序又如何把出口参数带回主程序。
3,子程序嵌套与递归例 11 求数组之和主程序名为 MAIN,子程序名为 SUM。
采用堆栈传递参数。
程序如下:
STACK SEGMENT PARA STACK
SPAE DW 20 DUP(?)
TOP EQU LENGTH SPAE
STACK ENDS
DATA SEGMENT
ARY1 DB 100 DUP(?)
SUM1 DW?
ARY2 DB 150 DUP(?)
SUM2 DW?
DATA ENDS
MAIN SEGMENT
ASSUME CS,MAIN,DS,DATA,SS,STACK
MOV AX,DATA
MOV DS,AX
MOV AX,SIZE ARY1
PUSH AX
MOV AX,OFFSET ARY1
PUSH AX
CALL SUM
……
MOV AX,SIZE ARY2
PUSH AX
MOV AX,OFFSET ARY2
PUSH AX
CALL SUM
HLT
MAIN ENDS
PROCE SEGMENT
ASSUME CS:PROCE,DS:DATA,SS:STACK
SUM PROC FAR
PUSH AX
PUSH BX
PUSH CX
PUSH BP
MOV BP,SP
PUSHF
MOV CX,[BP+14]
MOV BX,[BP+12]
MOV AX,0
ADN,ADD AL,[BX]
INC BX
ADC AH,0
LOOP ADN
MOV [BX],AX
POPF
POP BP
POP CX
POP BX
POP AX
RET 4
SUM ENDS
例 12,求阶乘主程序名为 MAIN,
子程序有二个:
FACT为阶乘子程序,
MULT为字节数相乘子程序,实现 CL× DL=AX。
主程序:
MAIN,MOV AX,3 ;设 n = 3
CALL FACT
XI,MOV BX,DX
HLT; 阶乘子程序 FACT;入口参数,AL中存放 n;出口参数,DX中存放 n!;所用寄存器,CX
FACT PROC
CMP AL,0
JNE IIA
MOV DL,1
RET
IIA,PUSH AX
DEC AL
CALL FACT
X2,POP CX
CALL MULT
X3,MOV DX,AX
RET
FACT ENDP;无符号字节数乘法子程序 MULT;入口参数,CL,DL中各为一乘数;出口参数,AX中为乘积
MULT PROC

RET
MULT ENDP
*本例既有子程序嵌套,又有子程序递归。
CALL FACT 是子程序递归,
CALL MULT 是子程序嵌套。
汇编语言程序举例例 1:将 ASCII码表示的两位十进制数转换成一字节二进制数 。
算法,37H,39H(ASCII 码 )→ 0000,0111B× 10+0000,
1001B→ 0100,1111B
DATA SEGMENT
ASDEC DB 37H,39H
BIN DB?
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
START,MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET ASDEC
MOV AL,[SI] ;取第一个数 ( 十位数 )
SUB AL,30H ;二进制数 ← ASC
SAL AL,1 ;乘 10
MOV BL,AL
MOV CL,2
SAL AL,CL
ADD BL,AL
INC SI
MOV AL,[SI] ;取第二个数 ( 个位数 )
SUB AL,30H ;二进制数 ← ASC
ADD AL,BL ;十位数 +个位数
MOV BIN,AL ;存入 BIN
MOV AH,4CH
INT 21H
CODE ENDS
END START
例 2:将 ASCII码表示的两位 16进制数转换成一字节二进制数 。
算法,41H,36H ( 即 ‘ A’,‘ 6’ ) → 0AH,
06H→A 0H,06H→A 6H(1010,0110B)
DATA SEGMENT
ASHEX DB 41H,36H
BIN DB?
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
START,MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET ASHEX
MOV AL,[SI] ;取第 1个数
SUB AL,30H ;转换成 16进制数
CMP AL,0AH
JB NEXT1
SUB AL,7
NEXT1,MOV CL,4 ;左移 4位
SAL AL,CL
MOV BL,AL
INC SI
MOV AL,[SI] ;取第二个数
SUB AL,30H ;转换成 16进制数
CMP AL,0AH
JB NEXT2
SUB AL,7
NEXT2,OR AL,BL ;两个十六进制数组合
MOV BIN,AL ;存结果
MOV AH,4CH
INT 21H
CODE ENDS
END START
例 3:将一字节二进制数转换成两位 ASCII码表示的十进制数 。
算法:先将二进制数转换成十进制数,转换方法是从二进制数中减去 10,每够减 1次,就将结果的十位数加一 。 直到不够减为止,这时的十位数内容就是二进制数中 10的个数,即十位数 。 最后一次的结果 ( 差 ) 为负,要加 10以恢复原值,这就是个位数 。
然后将这两个数 ( 十位数和个位数 ) 转换成 ASCII码 。
DATA SEGMENT
BIN DB 01001111B
ASDEC DB 2 DUP (?)
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
START,MOV AX,DATA
MOV DS,AX
MOV DI,OFFSET ASDEC
XOR AX,AX ; AH和 AL初值 =0
MOV AL,BIN
AGAIN,SUB AL,10
JB NEXT
INC AH ;够减,十位数加 1
JMP AGAIN
NEXT,ADD AL,10 ;恢复个位数
ADD AH,30H ;转换成 ASCII码
MOV [DI],AH
INC DI
ADD AL,30H
MOV [DI],AL
MOV AH,4CH
INT 21H
CODE ENDS
END START
例 4:将 ASCII码表示的 5位十进制数 ( 注:不大于
65535) 转换成两字节二进制数 。
算法:设 5位十进制数为 d4d3d2d1d0,其中 di为十进制数字,这样表示的数,其值为:
d4× 104+d3× 103+d2× 102+d1× 101+d0× 100
=0× 105+d4× 104+d3× 103+d2× 102+d1× 101+d0× 100
=[{[(0× 10+d4)× 10+d3]× 10+d2}× 10+d1]× 10+d0
由此可见,可先设一个寄存器 AX为 0,然后重复作乘以 10+d的运算,因为是 5位数,故需重复做 5次 ( 即循环 5次 ),就可得出结果 。
由于给出的是 ASCII码,故需先将其转换成二进制数。
DATA SEGMENT
ASDEC DB 33H,39H,36H,32H,35H
COUNT EQU $ - ASDEC
BIN DW?
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
START,MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET ASDEC
MOV CX,COUNT
XOR AX,AX ;部分和 AX,初值 =0
AGAIN,ADD AX,AX ; AX× 10
MOV BX,AX
ADD AX,AX
ADD AX,AX
ADD AX,BX
MOV BH,0
MOV BL,[SI] ;取 ASCII码,转换成二进数
SUB BL,30H
ADD AX,BX ;部分和计算
INC SI ;修改地址指针
LOOP AGAIN
MOV BIN,AX
MOV AH,4CH
INT 21H
CODE ENDS
END START
例 5:将 16位二进制数转换成用 ASCII码表示的 5位十进制数 。
算法:本题算法与前面例 3相同,只是例 3的位数较少 。 具体是先从 16位二进制数中减去 10000,够减的次数就是相应的万位数数字,由于最后不够减,故要加上 10000以恢复 。 然后依次用 1000,100,10作减数,求得各位的数字 。 最后将它们转换成 ASCII码 。
DATA SEGMENT
BIN DW 358CH
ASDEC DB 5 DUP (?)
PWTAB DW 10000,1000,100,10,1
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
START,MOV AX,DATA
MOV DS,AX
MOV DI,OFFSET ASDEC
MOV SI,OFFSET PWTAB
MOV AX,BIN
LOP1,XOR CL,CL
MOV BX,[SI]
LOP2,SUB AX,BX
JB NEXT
INC CL
JMP LOP2
NEXT,ADD AX,BX
ADD CL,30H
MOV [DI],CL
INC SI
INC SI
INC DI
CMP BX,1
JNZ LOP1
MOV AH,4CH
INT 21H
CODE ENDS
END START
例 6:编制完成两个 4字节的 BCD码 ( 8位十进制数 )
的加法运算程序 。
算法:将 4字节的 BCD码分为 4个单字节数相加,
从低字节开始,进行 4次循环操作,注意每次相加后必须进行 BCD修正。
DATA SEGMENT
BCD1 DB 76H,54H,38H,29H
BCD2 DB 49H,37H,65H,17H
SUM DB 4 DUP (?)
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
MULBCDADD,MOV AX,DATA
MOV DS,AX
LEA SI,BCD1
LEA BX,BCD2
LEA DI,SUM
MOV CL,4
CLC
AGAIN,MOV AL,[SI]
ADC AL,[BX]
DAA
MOV [DI],AL
INC SI
INC BX
INC DI
DEC CL
JNZ AGAIN
MOV AH,4CH
INT 21H
CODE ENDS
END MULBCDADD
例 7:有一个 100个字节的数据表,存放在数据段中,
首地址为 TAB,表内各数已按升序排列好 。 今给定一关键字,试编程从表内查找该关键字,若有,则结束;
若无,将该关键字顺序插入表中,并修改表长 ( 表长在 LTH中 ) 。
算法:将给定关键字依次同数据表中的数据比较,
若大于表中的数据,则继续同下一个数据比较,若小于表中数据,则先将从该数据开始的数据全部下移一个地址,然后将关键字插入表中空格,并将表长数加 1。
DATA SEGMENT
LTH DB 100 ;表长
TAB DB 0FH,12H,14H,… ;数据表
TEM DB 57H ;关键字
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA,ES,DATA
START,MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV BX,OFFSET TAB ; BX指向数据表
MOV AL,TEM ;取给定关键字
MOV CL,LTH ;取表长
MOV CH,00H
LOP,CMP AL,[BX] ;在表中查找
JE SOP ;找到,结束
JL INST ;未找到,若给定关键字小于表内;元素,转 INST,插入
INC BX
LOOP LOP
JMP JUST ;给定关键字一直大于表内元素,;将关键字放在表末
INST,MOV DI,OFFSET TAB
STD
ADD DI,LTH
MOV SI,DI
DEC SI
REP MOVSB
JUST,MOV [BX],AL
INC LTH
SOP,MOV AH,4CH
INT 21H
CODE ENDS
END START
例 8:有一数组含有 N个数,要求用气泡排序法将其按升序排列 。 为便于分析,设该数组中仅有 10个数,其值为:
36,-17,90,-8,-80,-19,125,-20,00和 50。
算法:采用两两比较的方法,先拿第 N个数 eN与第
eN-1个数比较,若 eN> eN-1,则不变动;反之,则交换。
然后拿 eN-1与 eN-2相比,按同样方法决定是否交换,这样一直比较到 e2与 e1比较。当第一次大循环结束时,数组中最小值冒到顶部。但数组尚未按大小排列好,还要进行第二次大循环,循环结束时,数组中第 2小值也上升到顶部的相应位置 … 如果在一个大循环中,一次交换都未发生,或只在 eN与 eN-1相比较时发生过交换,则说明数组在此大循环中已经有序,这样下一次大循环就不再需要,因此可以设置一个交换标志,而把其初值设为 N(数组长度,即每次大循环中小循环变量 J的初值)。未发生交换,标志不变;若发生交换,则把发生交换时的小循环变量 J值赋予标志。在一个大循环结束时,检查标志,若其值为 N(即一次交换都未发生,
或仅在 eN与 eN-1发生过一次交换),则停止排序。
DATA SEGMENT
BUFFER DW 36 -17,90,-8,-80,-19,125,-20,00,50
COUNT EQU $ - BUFFER
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
START,MOV AX,DATA
MOV DS,AX
MOV DX,2 ;大循环变量 I
CONT1,MOV CX,COUNT
MOV SI,CX
DEC SI
DEC SI
SHR CX,1 ;小循环变量 J
MOV BX,CX ;交换标志
AGAIN,MOV AX,BUFFER[SI]
CMP AX,BUFFER[SI-2]
JGE NEXT
XCHG AX,BUFFER[SI-2]
MOV BUFFER[SI],AX
MOV BX,CX ;置交换标志为 J
NEXT,DEC SI
DEC SI
DEC CX
CMP CX,DX
JGE AGAIN
CMP BX,COUNT/2
JE DONE
INC DX
JMP CONT1
DONE,MOV AH,4CH
INT 21H
CODE ENDS
END START
例 9:在已排好序的数据表中查找某个关键字,采用
,对分查找法,。
算法:设数据表中有 N个数据升序排列,查找的关键字为,M”。先取表中间值 eN/2(即 N/2处的值),将
eN/2与 M比较,若 eN/2=M,则已搜索到;若 M> eN/2,则下一次取 N/2~ N之间的中间值 e3N/4与 M比较;若 M< eN/2,
则下一次取 0~ N/2之间的中间值 eN/4与 M比较。这样,
每查找一次,使区间缩小一半,一直进行下去,直到①
被搜索的关键字已找到,或者②搜索区间变为 0,表示未搜索到。
DATA SEGMENT
BUFFER DB‘A C D G I N R S T U V W X Y Z’
COUNT EQU $ - BUFFER
PTRN DW?
CHAR EQU ‘M’
DATA ENDS
STACK1 SEGMENT PARA STACK ‘STACK’
STAPN DB 100 DUP (?)
STACK1 ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
START,MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET BUFFER ;区间上限 L
MOV CX,COUNT
MOV DX,1 ;查找次数初值
MOV AX,SI
ADD AX,CX
MOV DI,AX ;最后数加 1为下限 R
MOV AL,CHAR
CONT1,MOV BX,SI
ADD BX,DI
SHR BX,1 ;中间序号 J=( R+L) /2
CMP AL,[BX] ; eJ与关键字比较
JZ FOUND
PUSHF
CMP BX,SI ; J=上限吗?
JZ NOFID ; J=上限,区间为 0,未找到
POPF
JL LESS ;关键字< eJ
MOV SI,BX ;关键字> eJ,J作上限 L
JMP NEXT
LESS,MOV DI,BX ; J作下限 R
NEXT,INC DX ;查找次数加 1
JMP CONT1
NOFID,MOV DX,0FFFFH ;未找到,标记 0FFFFH
POPF
FOUND,MOV AX,DX
MOV PTRN,AX ;结果送 PTRN
MOV AH,4CH
INT 21H
CODE ENDS
END START
4.2.5 系统功能调用
DOS为程序设计者提供了许多可用 INT 21H指令直接调用的子程序 。
调用方法是:
(1)入口参数送指定的寄存器 ;
(2)功能号 (即子程序编号 )送 AH;
(3)INT 21H指令有的调用可以不用第 1步 。
要求掌握下面几种功能调用 。
1,01号功能调用读取键盘字符并显示
* 调用方法,MOV AH,01H
INT 21H
* 说明:扫描键盘,等待按键;若有键按下,检查按键码,当按下 CTRL-BREAK键时,则退出命令执行;当按下其它键时,则将对应的 ASCII码送 AL寄存器,同时送显示器显示。
2,02号功能调用显示字符
* 调用方法,要输出显示的字符送 DL
MOV AH,02H
INT 21H
* 说明:完成从标准输出设备显示器上显示一个字符。
例:利用 02号功能调用完成输出显示一串字符信息。
CODE1 SEGMENT
ASSUME CS,CODE,DS,CODE
ORG 100H
START,JMP BEGIN
MSG DB ‘This message was displayed with DOS function 02H’
MSGLEN EQU $-MSG
BEGIN,MOV AX,CODE
MOV DS,AX
MOV CX,MSGLEN
MOV SI,0
MOV AH,02H
NEXT,MOV DL,MSG[SI]
INT 21H
INC SI
LOOP NEXT
MOV AH,4CH
INT 21H
CODE ENDS
END START
3,09号功能调用字符串输出显示
* 调用方法:要输出显示的字符串的首地址送 DS,DX;
MOV AH,09H
INT 21H
* 说明,该功能调用完成在显示器上输出显示一字符串:
字符串必须以字符‘ $’(24H)为结束标志,但‘ $’字符不显示。
例,采用 09H号功能调用,输出显示指定的字符串
CODE SEGMENT
ASSUME CS:CODE,DS,CODE
ORG 100H
START,JMP BEGIN
STRING DB ‘Hello,How are you!’,’ $’
BEGIN,MOV AX,CODE
MOV DS,AX
LEA DX,STRING
MOV AH,09H
INT 21H
MOV AH,4CH
INT 21H
CODE ENDS
END START
4,0A号功能调用 — 字符串输入调用方法:
从键盘接受字符的输入缓冲区首地址送 DS,DX
说明:
(1) 完成从键盘接收字符串存到指定内存的输入缓冲区,直到输入回车符为止。
(2) 缓冲区的第一个字节为指定缓冲区能接收的字符个数,第二个字节保留以用作填写实际输入字符的个数,第三个字节开始存放从键上输入的字符。
(3) 若实际输入的字符数少于定义的字节数,则缓冲区内其余字节补零;若实际输入多于定义的字节数,则多余字符丢弃,且响铃。
例,利用 0AH号功能调用,从键盘输入最多达
80个字符的字符串存入以 BUF为首址的缓冲区中。
DATA SEGMENT
BUF DB 80
DB?
DB 80 DUP (?)
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,CODE
START,MOV AX,DATA
MOV DS,AX
LEA DX,BUF
MOV AH,0AH
INT 21H
MOV AH,4CH
INT 21H
CODE ENDS
END START