微机与接口技术笔记(3)
依然是第三章的内容,上一篇整理完了8086指令系统中主要的指令,这次主要讲程序格式和程序结构
汇编语言程序格式
汇编语言源程序汇编成目标平台的可执行文件的步骤是:
- 编写源程序,保存到xxx.asm或者xxx.a文件
- 通过汇编程序转换成xxx.obj或者xxx.o文件
- 通过连接程序连接成xxx.exe或者xxx.out文件
汇编语言程序书写格式
汇编语言源程序的结构采用分段结构形式,一个汇编语言源程序时由若干个逻辑段组成,每个逻辑段由SEGMENT语句开始,由ENDS语句结束。整个源程序以END语句结束。通常,一个汇编语言源程序一般应该由3个逻辑段组成,即数据段、堆栈段和代码段。
作为汇编源程序主模块,以下几部分不可缺少:
- 必须使用ASSUME伪指令告诉汇编程序,哪一段和哪一个段寄存器对应,即某一段地址应该放入哪一个段寄存器。这样对源程序模块进行汇编时,才能确定段中各项的偏移量。
- DOS的装入程序在装入执行时,将把CS初始化为代码段地址,把SS初始化为堆栈段地址,因此在源程序中不需要再对它们进行初始化。
对数据段初始化语句如下:1
2MOV AX,DATA(数据段段名)
MOV DS,AX - 在DOS环境下,通常采用DOS的4CH号中断功能调用使汇编语言返回DOS,即采用如下两条指令:
1
2MOV AH,4CH
INT 21H
8086宏汇编MASM使用的语句有3种类型:指令语句、伪指令语句和宏指令语句。
汇编语言程序中的每个语句可以由四项组成,格式如下:
[名字]: 操作符 操作数,操作数 ;注释
其中各项之间必须用空格(space)符隔开,名字项与操作数项间一般使用“∶”作分隔符,操作数项之间一般使用“,”作分隔符,操作数项与注释项间使用“;”作分隔符。
1 | START∶ MOV AX,1234H;立即数送往寄存器AX |
名字项
名字项是一个符号,它表示本条语句的符号地址。一般来讲,名字项可以是标号或变量。它是由非数字开头的字符串,可由下列字符组成:
- 字母 A~Z,a~z。
- 数字 0~9。
- 专用字符 ?、· 、@、-、$。
除数字外,所有字符均可以作名字的开始字符;专用字符“·”只能作为标号的开始符号。
操作符项
操作符项可以是指令、伪指令或宏指令的助记符。伪指令没有对应的机器码,只是在汇编过程中完成相应的控制操作。
操作数项
操作数项由一个或多个表达式组成,多个操作数项之间一般用逗号分开。
注释项
注释项用来说明一段程序、一条或多条指令的功能,提高程序的可读性,便于程序的阅读。
表达式与运算符
表达式由常数、寄存器、标号、变量与一些运算符组合而成,可以有数字表达式和地址表达式两种。
8086汇编语言中的运算符:
算术运算符 |
逻辑与移位运算符 |
关系运算符 |
分析运算符 |
属性运算符 |
+(加法) |
AND(与) |
EQ(相等) |
SEG(求段基值) |
PTR |
-(减法) |
OR(或) |
NE(不相等) |
OFFSET(求偏移量) |
“:”段运算符 |
*(乘法) |
XOR(异或) |
LT(小于) |
TYPE(求变量类型) |
THIS |
/(除法) |
NOT(非) |
GT(大于) |
LENGTH(求变量长度) |
SHORT |
MOD(求余) |
SHL(左移) |
LE(小于或等于) |
SIZE(求字节数) |
HIGH |
SHR(右移) |
GE(大于或等于) |
LOW |
伪指令语句
伪指令是控制汇编过程的命令,又称为汇编控制命令。它具有数据定义、存储区分配、指示程序的开始与结束等功能,但是没有对应的机器码。将汇编语言源程序翻译为目标程序后,其作用消失。
段定义伪指令
此伪指令实现存储器的分段管理,在汇编和连接程序时,控制不同段的定位类型、组合类型与连接,形成一个可执行程序。
常用的段定义伪指令有SEGMENT、ENDS和ASSUME等。
SEGMENT/ENDS伪指令
指令格式
1 | 段名 SEGMENT [定位类型][组合类型][类别] |
定位类型(align_type)说明段的起始地址应是如何的边界值。
PARA |
该段的必须从小段边界开始。即段的起始地址最低4位必须为0,应为xxxx0H。 |
BYTE |
该段可以从任何地址开始。 |
WORD |
该段必须从字的边界地址开始,即段的起始地址必须为偶数。 |
PAGE |
该段必须从页的边界地址开始,即段的起始地址最低8位必须为00,应为xxx00H。 |
组合类型(combine_type)说明程序连接是段合并方法。
PRIVATE |
私有段,在连接时将不与其他模块中的同名分段合并。组合类型的默认项。 |
PUBLIC |
连接时,对于不同程序模块中逻辑段,如果具有相同的类别名,就把这些段顺序连接成为一个逻辑段转入内存。 |
COMMON |
连接时,对于不同程序逻辑段,如果具有相同的类别名,则都从同一个地址开始装入,因而各个逻辑段将发生重叠。最后,连接以后的段的长度等于原来的逻辑段的长度,重叠部分的内容是最后一个逻辑段的内容。 |
STACK |
本段是堆栈的一部分,连接程序将所有STACK段按照与PUBLIC段的同样方式进行合并。这是堆栈段必须具有的段组合。 |
AT表达式 |
使段地址时表达式所计算出来的16位值。但它不能用来指定代码段。 |
MEMORY |
与PUBLIC相同。 |
类别(’class’)在引号中给出连接时组成段的类型名。当连接程序组织段时,将所有的同类别段相邻分配,段类别可以是任意名称,但必须位于单引号中。
ASSUME伪指令
指令格式:ASSUME 段寄存器名:段名[,段寄存器名:段名[,...]]
对于8086/8088 CPU而言,汇编过程中,ASSUME伪指令只是设定了段寄存器(CS、DS、SS和ES)应指向哪一个程序段,并没有给各个段寄存器装入实际值(CS除外)。因此,必须在程序中安排为段寄存器赋值的指令。段名必须与SEGMENT定义的相对应。
1 | DATA SEGMENT ; 数据段 |
数据定义伪指令
数据定义伪指令通常用来定义一个变量的类型,并将所需要的数据放入指定的存储单元中,也可以只给变量分配存储单元,而不赋予特定的值。
常用的数据定义伪指令有DB、DW、DD、DQ和DT。配合定义数据伪指令的重复操作符DUP(duplication operator)伪指令。
定义字节变量伪指令DB
DB(Define Byte)用于定义变量的类型为字节变量BYTE,并给变量分配字节或字节串,DB伪指令后面的操作数每个占用一个字节
定义字变量伪指令DW
DW(Define Word)用于定义变量的类型为字变量WORD,并给变量分配字或字串,DW伪指令后面的操作数每个占用1个字,即2个字节。在内存中存放时,低位字节存放在低地址中,高位字节存放在高地址中。
定义双字变量伪指令DD
DD(Define Double word)用于定义变量的类型为双字变量,DD伪指令后面的操作数每个占用2个字,即4个字节。在内存中存放时,低位字节存放在低地址中,高位字节存放在高地址中。
定义四字变量伪指令DQ
DQ(Define Quadruple word)用于定义变量的类型为四字变量,DQ伪指令后面的操作数每个占用4个字,即8个字节。在内存中存放时,低位字节存放在低地址中,高位字节存放在高地址中。
定义十字节变量伪指令DT
DT(Define Ten byte)用于定义变量的类型为十字节,DT伪指令后面的操作数每个占用10个字节。一般用于存储压缩的BCD码。
数据定义伪指令后面的操作数可以是常数、表达式或字符串,但每项操作数的值不能超过由伪指令所定义的数据类型限定的范围。
例:
1 | DATA SEGMENT ;定义数据段开始 |
DUP(duplication operator)用于重复某个(或某些)操作数。
[变量名] DB/DW/DD/DQ/DT〈表达式1〉 DUP〈表达式2〉
〈表达式1〉为重复次数,〈表达式2〉为重复的内容.
1 | MEM1 DB 3 DUP (4,5);从MEM1地址单元开始存放三组“04H,05H”共6个地址单元 |
符号定义伪指令
符号定义伪指令的用途是给一个符号重新命名,或定义新的类型属性等。
EQU伪指令
指令格式:名字 EQU 表达式
EQU伪指令的作用是将表达式的值赋给一个名字,以后可以用这个名字来代替上述表达式。
1 | COUTN EQU 200 ;COUNT代替常数200 |
=(等号)伪指令
“=”(等号)伪指令的功能与EQU伪指令基本相同,主要区别在于它可以对同一个名字重复定义,而EQU不能。
1 | COUTN EQU 200 ;正确,COUNT代替常数200 |
地址计数器$和ORG伪指令
地址计数器$
地址计数器的值可用$来表示,汇编语言允许用户直接使用$来引用地址计数器的值
ORG伪指令
ORG是起始位置设置伪指令,用来设置当前地址计数器的值。
指令格式:ORG 数值表达式
1 | DATA SEGMENT ;定义数据段开始 |
BIOS和DOS中断
基本输入输出系统(Basic Input/Output System,BIOS)给PC系列的不同微处理器提供了兼容的系统加电自检、引导装入、主要I/O设备的处理程序以及接口控制等功能模块来处理所有的系统中断。使用BIOS功能调用,程序员不必了解硬件的具体细节,可直接使用指令设置参数,调用BIOS示例程序,所以利用BIOS功能调用编写的程序简洁,可读性好,而且易于移植。
磁盘操作系统(Disk Operating System,DOS)是PC机上最重要的操作系统,它是由软盘或硬盘提供的。它的两个DOS模块IBMBIO.COM
和IBMDOS.COM
使BIOS使用起来更方便。
IBMBIO.COM
是输入输出设备处理程序,它提供了DOS到ROM和BIOS的低级接口,完成将数据从外设读入内存,或把数据从内存写到外设去工作。 IBMDOS.COM
包含一个文件管理程序和一些处理程序,在DOS下运行的程序可以调用这些处理程序。
DOS功能与BIOS功能都是通过软件中断调用。在中断调用前需要把功能号装入AH寄存器,把子功能号装入AL寄存器,除此之外,通常还需要在CPU寄存器中提供专门的调用参数。
BIOS和DOS的中断类型
不同微机系列的BIOS和DOS的中断类型各有差异。下面给出的是IBM PC系统主要的BIOS中断类型和DOS中断类型
CPU中断类型 |
8259中断类型 |
BIOS中断类型 |
用户应用程序和数据表指针 |
0 除法错 1 单步 2 非屏蔽中断 3 断点 4 溢出 5 打印屏幕 6 保留 7 保留 |
8 系统定时器(IRQ0) 9 键盘(IRQ1) A 彩色/图形接口(IRQ2) B COM2控制器(IRQ3) C COM1控制器(IRQ4) D LPT2控制器(IRQ4) E 磁盘控制器(IRQ4) F LTP1控制器(IRQ4) |
10 显示器I/O 11 取设备信息 12 取内存信息 13 磁盘I/O 14 RS-232串口I/O 15 磁盘I/O 16 键盘I/O 17打印机I/O 18 ROM BASIC 19 引导装入程序 1A 时钟 40 软盘BIOS |
用户应用程序 |
1B 键盘终止地址(Ctrl+Break) 4A 报警(用户闹钟) 1C 定时器 |
|||
数据表指针 |
|||
1D 显示器参数表 1E 软盘参数表 1F 图形字符扩展码 41 0#硬盘参数表 46 1#硬盘参数表 49 指向键盘增强服务变换表 |
BIOS中断
功能调用号 |
功能说明 |
功能调用号 |
功能说明 |
20 21 22 23 24 25 26 |
程序终止 功能调用 终止地址 Ctrl+C中断向量 严重错误向量 绝对磁盘读 绝对磁盘写 |
27 28 29 2A 2E 2F 30~3F |
结束并驻留内存 键盘忙循环 快速写字符 网络接口 执行命令 多路转换接口 保留给DOS |
DOS中断
BIOS和DOS功能调用的基本步骤
- 将调用参数装入指定的寄存器。
- 如需要功能调用号,把它装入AH。
- 如需要子功能调用号,把它装入AL。
- 按中断号调用DOS或BIOS。
- 检查返回参数是否正确。
常见的BIOS和DOS功能调用
一般来说,实现同样的功能有时既可以用BIOS中断调用,也可以用DOS中断调用。
键盘输入中断调用
BIOS键盘中断
类型16H的中断提供了基本的键盘操作,它的中断处理程序包括3个不同的功能,可根据AH寄存器中的功能号来确定
AH |
功能 |
返回参数 |
0 |
从键盘读一字符 |
AL=字符码,AH=扫描码 |
1 |
读键盘缓冲区的字符 |
如ZF=0,AL=字符码,AH=扫描码;如ZF=1,缓冲区空 |
2 |
取键盘状态字节 |
AL=键盘状态字节 |
BIOS键盘中断(INT 16H)
DOS键盘中断
类型21H的中断提供了DOS键盘操作,它的中断处理程序包括7个不同的功能,分别根据AH寄存器中的功能号来确定
AH |
功能 |
调用参数 |
返回参数 |
1 |
从键盘输入一字符并回显在屏幕上 |
无 |
AL=字符 |
6 |
读键盘字符,不回显 |
DL=0FFH |
如ZF=0,AL=字符;如ZF=1,缓冲区空 |
7 |
从键盘输入一个字符,不回显 |
无 |
AL=字符 |
8 |
从键盘输入一个字符,不回显,检测Ctrl+Break |
无 |
AL=字符 |
A |
输入字符到缓冲区 |
DS:BX=缓冲区首地址 |
AL=字符 |
B |
读键盘状态 |
无 |
AL=0FFH,有键入 AL=00,无键入 |
C |
清除键盘缓冲区,并调用一种键盘功能 |
AL=键盘功能号 (1,6,7,8或A) |
DOS键盘中断(INT 21H)
应用
1号系统功能调用─从键盘输入一字符并显示在屏幕上
此调用的功能是系统扫描键盘并等待输入一个字符,有键按下时,先检查是否是Ctrl+Break键,若是则退出;否则将字符的键值(ASCII码)送入寄存器AL中,并在屏幕上显示该字符。此调用没有入口参数。
1 | MOV AH,1 ;1为功能号 |
6号系统功能调用─读键盘字符
此调用的功能是从键盘输入一个字符,或输出一个字符到屏幕。
如果(DL)=0FFH,表示是从键盘输入字符。
当标志ZF=0时,表示有键按下,将字符的ASCII码送入寄存器AL。
当标志ZF=1时,表示无键按下,寄存器AL中不是键入字符ASCII码。
如果(DL)≠0FFH,表示输出一个字符到屏幕。此时DL寄存器中内容就是输出字符的ASCII码。
1 | MOV DL,0FFH |
显示器输出中断调用
DOS显示中断的中断处理程序包括3个不同的功能,分别根据AH寄存器中的功能号来确定。
AH |
功能 |
调用参数 |
返回参数 |
2 |
显示一个字符(检验Ctrl+Break) |
DL=输出字符 |
无 |
6 |
显示一个字符(不检验Ctrl+Break) |
DL=0FF(输入) |
AL=输入字符 |
DL=字符(输出) |
无 |
||
9 |
显示字符串 |
DS:DX=串地址,字符串以‘$’结尾 |
无 |
2号系统功能调用─显示一个字符(检验Ctrl+Break)
此调用的功能是向输出设备输出一个字符码,此调用的入口参数是输出字符的ASCII码,入口参数需送入寄存器DL,没有出口参数。
1 | MOV DL,'A' ; “A”为要求输出字符的ASCII码 |
调用结果是将DL寄存器中字符‘A’通过屏幕显示(或打印机)输出
9号系统功能调用—显示字符串
它要求被显示输出的字符必须以“$” 字符作为结尾符。要显示输出的信息一般定义在数据段。
1 | MESSAGE DB ‘The sort operation is finished’,‘$’ |
子程序结构
子程序又称为过程,它相当于高级语言中的过程和函数。
子程序的结构形式
子程序的基本结构包括以下几部分:
- 子程序说明。
- 保护现场和恢复现场。
- 子程序体。
- 子程序返回。
子程序的定义
子程序又称为过程(procedure),过程要用过程定义伪指令进行定义。
指令格式:
1 | 过程名 PROC [NEAR] 或 [FAR] |
注意:
- PROC和ENDP必须成对使用
- 过程名是自定义符,可以作为标号被指令CALL调用
- 过程由RET指令返回,返回调用程序的操作与过程的属性有关。NEAR型过程属于段内调用,则RET是段内返回;FAR型过程属于段间调用,RET是段间返回。系统设定的缺省类型为NEAR。
调用程序和过程在同一个代码段,属于近调用,程序形式如下:
1 | MAIN PROC FAR ; MAIN为调用程序 |
这里的MAIN和DISPLAY分别为调用程序和子程序的名字。因调用程序和子程序在同一个代码段,所以DISPLAY选择了NEAR属性。这样,但MAIN调用DISPLAY保护返回地址时,只需保护IP指令就可以了。本例也可以这样写:
1 | MAIN PROC FAR ; MAIN为调用程序 |
也就是说,过程定义可以嵌套,一个过程定义可以包括多个过程定义。
调用程序和过程不在同一个代码段,属于远调用,程序形式如下:
1 | CODE1 SEGMENT |
子程序的参数传送
调用程序在调用子程序时,经常需要传送一些参数给子程序;子程序运行完成后也经常要回送一些信息给调用程序。这种调用程序和子程序之间的信息传送称之为参数传送(或称变量传送或过程通信)。参数传送方式主要分为以下三种。
- 通过寄存器传送参数。
- 通过堆栈传送参数。
- 通过存储器传送参数。
啊,第三章真的好长,知识点也很多,一章直接入门编程语言,以后还是得多看
练习题
- 汇编语言源程序经汇编、链接后生成
EXE
文件。- 8086/8088汇编语言中伪指令的定义如下:VAR DB 2DUP(1,2,3DUP(3),2DUP(1,0)) , 则在VAR存储区前10个单元的数据是
1,2,3,3,3,1,0,1,0,1
。- 微处理器8086的一个段的最大范围是
64
KB。- 指令AND AX,0 将累加器AX的内容
清零
。- 逻辑移位指令SHL用于
无符号数乘2
。- 设(SS)=2000H, (SP)=0100H, (AX)=2107H, 执行指令PUSH AX后,存放数据21H的物理地址为
200FFH
。
课后题
名称解释:操作数,操作码,立即数,寄存器操作数,储存器操作数,汇编,汇编程序,汇编程序语言,伪指令,中断。
答:操作数:操作数指出指令执行的操作所需要数据的来源。操作码:用来表示该指令应进行什么性质的操作。立即数:通常把在立即寻址方式指令中给出的数称为立即数。寄存器操作数:表示某一个寄存器,操作数本身存放于寄存器中,在指令中只是给出了几个位的代码来表示它具体存放在哪个寄存器中。储存器操作数:和寄存器操作数差不多,操作数本身放在储存器中。汇编:汇编指将汇编语言源程序转换成对应的机器语言程序。 汇编程序:指把汇编语言书写的程序翻译成与之等价的机器语言程序的翻译程序。汇编程序语言:是一种面向CPU指令系统的程序设计语言。伪指令:是控制汇编过程的命令,又称为汇编控制命令。中断:指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
请说明下列指令是否正确,并指出错误原因。
- MOV [2100],[2200H],错误,MOV指令的两个操作数必须有一个为寄存器
- MOV DH,0001H,错误,两个操作数的位数不一样
- MOV CX,50H[BX+BP],错误,不允许同时使用BX,BP
- MOV IP,2456H,错误,IP不能作为目标操作数
- PUSH DL,错误,DL为一个字节,不能进栈
- MOV CS,AX,错误,CX只能向通用寄存器传,不能反转
- MOV 3000H,BX,错误,立即数不能作为目标操作数
- MOV ES,DS,错误,不允许在两个段寄存器之间传值
- IN AX,256,错误,256不在00H~FFH范围之内