date
Sep 7, 2024
icon
password
Pin
Pin
Hide-in-Web
Hide-in-Web
网址
type
Post
slug
note-2
tags
专业课程
笔记
category
学习分享
bottom
bottom
Hide-in-Config
Hide-in-Config
comment
Show
status
Published
summary
周佳社,微机原理与接口技术,大三
🎉欢迎大家来到我的博客!!🎉
请大家尽量用电脑来进行查看,会有最好的显示效果。
课程资料
南理工老师们整理的微机课件真的很用心,因此也分享给大家,这是老师们的努力成果,仅供学习交流,勿做商用!!
P.S. MSP 432 也会考察一些基本概念,因此它的 ppt 也需要过一遍。微机原理的期末试卷据说是可以买到 22 年的,不过我没有,其他旧一些的试卷可以在文印店买到,这里分享 18 年的试卷,截至 24 年题型与 18 年一致。
上面的微机课件在很多细节方面都整理的很好,无论是否是南理的友友,都可以看一看,一定会对你有所帮助。
网课课程排布

绪论
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 课程安排
- 智能电子信息系统的设计
- 微型计算机系统的组成
- 早期计算机的组成
- 微处理器
- 存储器
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
课程安排
前四章的内容是关于软件的,主要是汇编语言的学习,前三章是基础知识的介绍,第四章是程序的设计,需要有 C 语言基础;
第五章到第十章的内容是关于硬件的设计,需要有数字逻辑电路的基础。

物理量经过传感器采样后转换为电量进行表示,电量一般是毫伏级的。电量通过放大、滤波等前端调理电路处理后会转换为合适的模拟信号。在实验室中,通常会留出一个测试点连接示波器用于测试采样的波形。模拟波形信号经过 AD 转换器可以转换为计算机适合处理的数字信号,数字信号会存储在 CPU 中。CPU 接受的信号都来自外部世界,但也通过这些信号控制外部世界。
由于经过 CPU 处理的数据传输到 DA 转换器后,有些是模拟量数据,有些是串行数据,有些是并行数据,因此需要有微机接口对数据进行转换以匹配具体的器件。
后端调理电路与前端调理电路是类似的,也能够进行放大和滤波的操作。
课程学习中应用到的 CPU 器件是 Intel 的 8086 芯片,但后面讲课会与 8088 对比。
微型计算机系统的组成
计算机系统都是由硬件子系统和软件子系统组成的。
早期计算机的组成

ALU (算术逻辑单元,A-算术,L-逻辑,U-单元):计算机硬件最基础的功能就是要能够进行运算,而运算既包括加减乘等算数运算,又包括与或非等逻辑运算,因此需要算术逻辑单元。
控制器:而为了能够操控 ALU 进行运算,就必须要有控制器的指挥。
存储器: 有了控制器和 ALU 后,我们需要有进行运算的数据,这些数据一部分存储在存储器中,由控制器进行调用,由 ALU 进行运算,而运算后的数据结果会通过数据线重新传回存储器中进行储存。
输入输出设备(I/O 设备):早期的计算机也常通过外部设备(也由控制器控制)来获取数据进行运算并输出,输出的数据也可存入存储器中。
在上图中,绿色的线称为控制线,传输的是控制信息,红色的线是数据线传输的是数据信息,还有一类信息是地址信息,用于控制器检索存储器中数据的位置(通过地址码)。而传输控制信息、数据信息、地址信息的线统称为总线(用于传输信息的通路),不同的信息需要不同的总线进行传递,传输控制信息的称为控制总线(CB),传输数据信息的称为数据总线(DB),传输地址信息的称为地址总线(AB)。
图中蓝色方框所示的内容即为中央处理器,通常集成在一块芯片中。
微型计算机
微处理器(CPU),顾名思义,大小必须是比较小的。微处理器在与外界进行通讯的过程中,需要通过引脚。微型计算机指的是以微处理器作为核心处理单元组成的小型计算机系统,包括了微处理器、内存(如 RAM 和 ROM)、输入输出接口(I/O 端口)、电源和其他必要的部件

引脚
引脚就是微处理器与外界进行通信的接口,引脚可以是控制总线,传输控制信息;可以是数据总线,传输数据信息;也可以是地址总线,传输地址信息。
复用引脚
如果一个处理器需要有几十个与外界通信的接口,那么引脚数量增加的同时,微处理器的大小必然也会增大,但为了保证微处理器的大小比较小,因此厂家们便引入了复用引脚,即一个引脚可以既可以用于输入数据,也可以输出数据,或是输出地址数据。但必须保证在某个数据传输时不会有其他数据混在一起。
微处理器级总线和系统总线

CPU 想要输出控制命令或是传输数据,就必须先发出地址数据指明地址,然后再输出。一般会将地址和数据引脚或控制引脚复用,在微处理器的第一个周期输出地址信息用于锁定操作的地址,后三个周期用于输出数据信息(或控制信息),微处理器一般是以四个周期为单位的。
对于复用引脚而言,它发出的信息需要通过系统总线形成电路区分并分配到不同的总线(数据总线,地址总线和控制总线),其中数据总线和控制总线是双向的,而地址总线只能单向输出,因为地址数据是 CPU 发送出来用于寻找特定的 I/O 设备(I/O 地址)或是某个存储器设备(存储器地址)的,传输的是地址码。
由微处理器传输出去的信息称为命令信息,由外部设备反馈回来的信息称之为状态信息。状态信息与命令信息合并起来统称为控制信息。
如果要设计系统总线形成电路,我们必须清楚 CPU 各个引脚的作用是什么,以及各个引脚之间信号的时序关系。这部分电路可以通过数电课上学过的锁存器、缓冲器等器件来完成(第五章的内容)。
任何器件都是通过挂接在系统总线上来实现与 CPU 的通信的,系统总线形成电路不仅解决了引脚复用分开的问题,同时也增强了 CPU 带动负载的能力(单个微处理器驱动负载的能力比较弱)。
存储器 ROM 和 RAM
任何系统的存储器都是 RAM 和 ROM 两个部分组成的。
RAM 通常储存的是临时性的数据,掉电后数据后丢失,而 ROM 存储的是系统程序的数据,掉电后不会丢失数据。
我们日常使用的计算机中的 ROM 是一种闪存类型的 EEPROM,其中存放了 BIOS(Basic Input Output System)程序。计算机通电后,BIOS 先对 RAM 执行自检(POST,Power-On Self Test)程序,检测硬件是否正常工作,RAM 中的每个比特位是否都是能写能读的。然后,BIOS 初始化主板上的 I/O 接口,并从引导设备中读取启动引导程序,接着由启动引导程序加载操作系统到内存中。
因此称 RAM 为数据存储器,ROM 为程序存储器。
RAM 和 ROM 具体如何与接口电路连接是 第六章 需要学习的内容。

一般来说,存储器单元是按照字节组织的,如果将整个学校看作是一个微机系统,行政楼就相当于是一个 CPU ,用于发出指令。而我们每个人都相当于是一个比特位。假设每 8 个人一个宿舍,学校布置的任务都是以宿舍为单位完成的。如果你的宿舍只有 4 个人,需要和其他 4 人的宿舍合并一起完成学校的任务。每个宿舍都有自己的门牌号(地址),学校需要找某个宿舍时,可以通过宿舍的门牌号来查找,对 CPU 就是通过地址线调用特定的地址(寻址)。
假如 CPU 有 4 根地址线,那么能够表示的地址空间的大小就是 .地址线的数目决定了 CPU 能够“管理”的外部空间的大小 。寻址的方式越多,汇编语言就越灵活,后面会专门介绍寻址的操作,寻址的方法有很多。
可以将上面的存储器列表左侧看作是地址(箭头处),列表的内容看作是存储的内容,内容可以是其他部分的地址(类似于二级指针),也可以是某个变量的值(数据)。在后面的课程中,多采用这种方式来表示存储器,列表上面是低地址,下面是高地址,低地址存放低位字节,高地址存放高位字节。
I/O 接口设计
微处理器通过系统总线形成电路与 I/O 接口电路进行交互。
CPU 发出的地址数据会通过 I/O 地址译码器选择外部的 I/O 设备,选择好设备后,CPU 能够接受特定的 I/O 设备发出的数据或是发出数据给特定的 I/O 设备。
这部分的内容是 第七章 需要学习的。
汇编语言的介绍
指令是一组二进制代码,用来指示 CPU 执行特定的操作。所有程序的代码最终都要翻译为计算机能够理解的二进制代码,即机器语言。代码中每一行语句都告诉 CPU 做一件事情,包括怎么做和做什么。
理论上,可以直接使用二进制机器代码编写程序,但这种方式编写的机器语言程序对程序员来说非常难以记忆和操作,造成编程效率低下。
为了解决这个问题,程序员们提出了用符号代码代替二进制代码编写程序的汇编语言。汇编语言通过汇编器(可以理解为“翻译器”)转换为机器语言(这个过程称为编译)供 CPU 执行,编写汇编程序比直接写机器代码更形象、易记。
计算机中的数制与码制(第一章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 各种进制的数的转换
- 二进制的运算
- 寄存器作用及分类
- 加法和减法
- PSW
- 与或非运算
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
计算机中存储和处理的数据都只能是二进制的数字,因此外界输入的数据最终都要转换成二进制数,需要了解数制之间的转换。本门课中侧重整数的数制转换,小数转换只需了解即可。这部分内容比较简单,因此笔记会比较少。
二进制数 ↔ 十进制数
计算机只能理解二进制,而用户需要阅读十进制数,因此二者的相互转换是比较重要的。
十进制数转换为二进制数可以采用短除法。老师推荐使用权逼近的方法,速度更快。


二进制数转换为十进制数可以采用位置加权法。如 8 位二进制数的每一位都有一个权重,最高位是 ,次位是 ,以此类推。
但是,由于位置加权法在计算机中的运算效率比较低,因此在写代码的时候基本不会使用位置加权法,多采用除 10 取余法。
首先介绍什么是取余运算:
这个方法利用到计算机中的一个运算符 %,只取余数,整数部分会被丢弃。比如,11 除余 4(11 % 4)正常除法是得到 2余 3,而除余运算会将整数丢弃,只留下 3;101 % 10 会得到 1.
通过除余运算,可以很容易得到十进制数的百位、十位和个位。
比如 143,它的个位是 143 % 10 = 3,十位是 (143 / 10)% 10 = 4,百位是 (143/100)%10 = 1;
那么为什么二进制转换为十进制需要使用除 10 取余法呢?
我觉得老师想描述的应该是二进制转换为十进制 BCD 码需要采用除十取余法。
在计算机中,每个数都是通过二进制存储的,比如 93 这个数,二进制是 0101_1101,如果我们需要将这个数转换为十进制数并输出,我们就需要先将其除余 1010 (十进制中的 10)得到个位,个位就是 0000_0011,将其放入变量 a 中。 然后再将 93 先除以 1010,再除余 1010 得到 0000_1001,将其放入变量 b 中。
当我们需要将 93 这个数以十进制形式输出时,就需要变量 b 作为十位,变量 a 作为个位,这样就得到了 93 的 BCD 码,可以显示在屏幕上正常显示。
在计算机中,从键盘输入一个十进制数的时候,内部会自动将其转换为二进制数,如果输入的是带十位、百位的数,那么内部会将前一次输入的数自动乘以 1010(十进制为 10),然后再加上新输入的数,这样就实现了十进制转换为二进制。
十进制 ↔ 十六进制
先将 十进制 → 二进制,然后再将二进制 → 十六进制(四位一组)。
十六进制转换为十进制就将过程逆过来即可。
二进制的运算(寄存器)
只要发生了二进制的运算,就会影响 PSW 状态标志位,PSW 是什么在后面有介绍。
算术运算
本门课中更多用到加法运算、减法运算,对于乘法和除法运算仅作了解。
在进行加法、减法运算的时候,首先需要弄清楚进行运算的数据的字长是多少,假如有两个字长为 8 位的十进制做加法运算,如下图所示:

首先需要将它们都转换为二进制数,然后再相加。为了提高计算机内部运算的速度,需要在计算机内部安放寄存器,用于暂存数据。CPU 内部寄存器数量越多,速度越快,但相应地其体积也会越大。
寄存器和 RAM 和 ROM 是不一样的,RAM 和 ROM 是在 CPU 外部的,属于是外部存储器,由于并不在 CPU 内部,因此速度没有寄存器快。寄存器 和 RAM 都是易失性存储器,断电后数据会丢失。
8086 CPU 内部的寄存器主要有以下 8 种
8 位寄存器的分类

将 AH 和 AL 合并起来就为 Ax,他们在写代码时可以分开使用,也可以直接使用 AX。
如果需要进行上述的加法运算,写程序时需要这样写(这里的程序部分可以先了解,在后面会有详细介绍):
在编程过程中,为了准确的区分数字和名称,程序员们制定了这样的规则,名称不能以数字开头,高进制的数字不能以字母开头。比如,十六进制数 BAH,在写程序时要写为 0BAH,否则就会被识别为是变量。
如果进行有符号数的运算,需要考虑“溢出”的问题,溢出指的就是两数相加或两数相减后的结果超出了二进制位数所能表示的最大范围。关于溢出的更多介绍可以进入这个页面 『溢出』.
溢出的例子和上面举的例子相同,只不过上面是无符号数,这里是有符号数,可以看到两个正数相加的结果得到了一个负数。

在计算机中,通过下面这个框图来决定最终的运算结果,在运算过程中会进行状态标志位的设置。比如如果两个有符号数相加发生了溢出,那么溢出标志位就会设置为 1。

标志位总共有 6 个(具体如下方的折叠框所示),通过处理器状态字寄存器(也称标志寄存器)储存,关于状态标志位的内容在后面课程也会详细介绍,这里只是引入。
什么是微处理器状态字(PSW, processor state word) ?(教材 P18- P19)

设置 PF 的意义在于在进行串口通信传输数据的时候,为了保证接收方接收的数据和发送方发送的数据大概率是相同的,需要事先确定一个奇标志,或是偶标志,这样如果发送方发送的是奇数个 1,而接收方接收到了偶数个 1,那么数据在传输过程中肯定发生了更改。之所以说是大概率,是因为如果数据中恰好有偶数个数据发生了变化,那么接收方也无法判断发送方的数据是否发生了更改。

逻辑运算
逻辑运算在进行描述的时候都是使用
助记符 SRC, DST
这种方式进行的。- SRC 是 Source 的缩写,表示源寄存器/存储单元。
- DST 是 Destination 的缩写,表示目标寄存器/存储单元。
逻辑指令一般是让 DST 与 SRC 一起做助记符所进行的运算。下面将介绍逻辑与、或、异或和非指令。
逻辑与操作:

逻辑与的应用场合:当我们希望让变量中的某些位清零,其他位保持不变时使用。
逻辑或运算:

逻辑或的应用场合:当我们希望让变量中的某些位置 1 ,其他位保持不变的时候使用。
逻辑异或运算:

逻辑异或的应用场合:当我们希望让变量中的某些位取反,其他位保持不变的时候使用,其中取反的位使用 1 进行异或,不变的位使用 0 进行异或。
逻辑非运算:

上述的四种运算,除了逻辑非运算以外,其余运算都会对 PSW 状态标志位有影响。
第一版 P89 例3.10

第一小题就使用到了 AND,可以使用
AND AX, 0FFF0H
来实现功能;第二小题需要使用到了 OR 指令,可以使用
OR BX, 000FH
来实现;第三小题使用到了 XOR 指令,可以使用
XOR AL, 0F0H
来实现;有符号数的表示范围及溢出问题
对于有符号数,最初使用原码来表示,正数的原码为其本身,负数的原码是其最高位取 1,其余位不变;
如果有符号数使用原码来表示,会出现以下两个问题:
- 0 的原码有正负之分,这显然不符合实际;

- 正数和负数之和不为 0.
基于上述两个问题,引入了补码这种表示方法,为了要实现正数和负数和为 0 ,定义原码转换为补码的方式为:

但在实际应用中,如果都采用上面的方式计算补码显然比较麻烦,于是从上面的运算中总结出原码转换为补码的规律,也就是说,原码的符号位不变,其余位按位取反后在末尾加上 1 就可以得到其补码:
同时,由于 0 的补码就是其本身,第一个问题也得到了解决。
在编写程序的时候,用户并不需要将十进制数转换为补码的形式写入代码中,直接写整数就可以了,汇编程序会自动进行十进制数转换为补码的过程,如 要将 -3 移入 AL 寄存器中,只需要写
MOV AL, -3
即可。补码的表示范围:

补码的运算及溢出:


补码运算的公式一: ;
补码运算的公式二: ;
溢出的概念前面有介绍过,当补码数运算后的数的范围超出了上述图片中补码所能表示的数的范围时,会发生溢出。
也可以通过下图所示的方式来判断溢出:

如果最高位(符号位)和次高位同时发生了进位或者都不发生进位,那么不会发生溢出,如果只有其中一个发生了进位,那么就会发生溢出。上图的过程没有发生溢出,下面举一个可以发生溢出的例子:

在程序中如果发生上述溢出的过程, PSW 会如何变化呢?
补码运算的公式三: ;
公式三是由公式一和二推导出来的,它意味着两数的减法运算可以转换为相应补码的加法运算,在 CPU 中,做加法运算比减法运算更快。
补码还有一个公式比较重要, ,也就是说,A 的补码取反加 1 后可以得到 A 的原码。
变补/求负
所谓的变补或是求负,指的就是已知一个数的补码,求其负数的补码的过程。
要实现上面的过程,只需要对该补码再取其补码即可,通过下面的示例可以更理解这一点:

-(-3)= 3,3 的补码就是其本身
BCD 数
BCD 的含义是 Binary-Coded Decimal,即二进制编码十进制。而实现 BCD 有多种方式,如格雷码,8421 码。
如果使用 8421 码编码,在计算机内部存储时,有组合 BCD 的方式和分离BCD 的方式两种进行存储。加法和减法既有组合 BCD 数的运算,也有分离 BCD 数的运算;而乘法和除法只有分离 BCD 数的运算。
组合 BCD 数和分离 BCD 数在程序中的存储方式可以通过下图来理解:

使用不同的方式来表示 BCD 数,运算后结果采用的调整方法就不同。这里指的调整是指在 BCD 码发生运算的时候,由于 BCD 码的进位与十六进制的进位规则是不同的,会造成结果的错误,因此需要对结果进行调整修正。BCD 码的进位是“逢十进一”,而十六进制的进位规则是“逢十六进一”。
如下图所示,53 H 和 28 H 分别表示的是组合 BCD 数 53 和 28,其中 5、3、2、8 都使用十六进制来表示,在发生运算时,如果没有进行修正,默认采用十六进制的进位规则,得到的结果为 7b H ,这显然是错误的,观察结果可以发现 b 超出了 BCD 码所能表示的范围 0~9,从 BCD 的角度来说要发生进位,通过对低 8 位 +6 修正后,结果变为 81H,这个结果是正确的。

为什么是进行 “+6” 修正呢?
因为 BCD 码最大表示的数为 9,而十六进制最大为 15,它们相差 6。
与 DAA 对应的还有 AAA ,用于分离 BCD 数的修正,具体如下:

ASCⅡ 码
计算机中的数字都采用二进制表示,字符都采用 ASCⅡ 码表示。ASCⅡ 码的第一位都是 0,也就是说 ASCⅡ 码 使用 7 位二进制数表示字符。
在本门课中需要掌握的 ASC Ⅱ 码:
可显字符:
数字 ‘0’ ~ ‘9’ (加上单引号表示是一个字符):30H ~ 39H 。
字母 ‘A’ ~ ‘F’ :41H ~ 47H 。
字母 ‘a’ ~ ‘f’ :61H ~ 67H 。
如果要将数字字符和字母字符的 ASC Ⅱ 码转换为十六进制数字,则数字字符应该减去 30H,字母字符应该减去 37H。
而字母大小写之间的相互转换一般采用逻辑运算来实现。
大写字母与小写字母相互转换的例题
大写字母 → 小写字母:

在第一版教材 P89 的例3.10中,第 6 小题就是将 AL 中的ASCⅡ 码转换为相应的大写字母的 ASCⅡ 码。
转换的过程其实有两点需要实现:
- 第一点是如果 AL 中存放的就是大写字母,那么保持不变;
- 第二点是如果 AL 中存放的是小写字母,那么需要将其转换为大写字母。

将字母 A ~ F 和 字母 a ~ f 的 ASC Ⅱ 写出来后,会发现,他们的低 4 位是一一对应的,高四位中只有第 3 位是不变的。

第 6 小题可以通过
AND AL, 0DFH
(0DFH → 1101_1111H)或 AND AL, 5FH
(5FH → 0101_1111H)来实现,后者是因为 ASCⅡ 码的最高位是始终为 0 的。小写字母→大写字母:
和大写转小写的分析是一样的,只不过要进行的是或运算,因为需要将高 4 位中的第 3 位保持为 1 或由 0 转换为 1,具体的指令为
OR AL, 20H
。非可显字符:
回车: 0DH
换行: 0AH
空格:20H
作业
只有本节视频说了作业,后面的视频好像都没说。
作业 1.9、1.10(1 5 7)、1.11(1 2 5)、1.12(1 7)、1.13(2 3)、1.15(3 7)
8086 的结构与功能(第二章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 8086 结构
- 外部结构
- 内部结构
- 堆栈
- 微处理器的功能结构
- BIU 部分理解
- EU 部分理解
- 微处理器的寄存器组织
- 存储器地址空间与数据存放格式
- 地址空间
- 数据存放格式(字型、字节型等)
- 存储器分段与物理地址形成
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
8086 结构介绍
微处理器的外部结构
外部结构表现为数量有限的输入输出引脚,这些输入输出引脚构成了微处理器总线,分为数据总线、地址总线和控制总线。
8086/8088 采用的是双列直插式的封装,有 40 个引脚,引脚的分布图如下图所示:


CPU 中的引脚引出后,因为 CPU 本身是一个弱机,且需要将复用引脚中传输的信息分开,还需要连接系统总线形成电路,图中没有画出。
图 5.5 中的第 28 个引脚用于选择 CPU 是对内部的存储器(Memory)操作还是对 IO 端口进行读/写操作,读操作通过 来控制。


8086 CPU 有 16 条数据总线、 20 条地址总线和 16 条控制总线。 ~ , ~ 共 20 个引脚是专门传输地址信息的,前者既能够给外部存储器提供地址,也可以给 IO 提供地址,而后者只能给外部存储器提供地址。因此,能够给外部存储器分配的内存是 1 MB(),给 IO 分配的内存是 。 ~ 总线究竟是给存储器提供地址还是给 I/O 提供地址需要通过上面的 线路来决定。
I/O 端口是连接 CPU 与 I/O 设备的控制电路的,在 I/O 端口中,有一个 I/O 端口寄存器,用于与 CPU 之间进行数据交换,cpu 会为 I/O 接口电路中的每个端口附一个端口地址。I/O 接口电路的结构如下图所示:

外部设备首先将自身的状态输入到状态输入端口,CPU 通过 引脚选定这个端口后,可以读取这个端口的数据,如果未选定这个端口,即便这个端口中有设备的状态数据,也会处于高阻态。在 cpu 有空闲可以处理外部设备的信息时,会读取状态输入端口的数据,确认设备是否准备好,如果设备准备好了,那么 CPU 会将命令信息和数据信息分别通过不同的数据总线传输给命令端口和数据输出端口。
I/O 端口本质上就是一种传输信息的通道,一个 I/O 端口就是一个通道。一个 I/O 端口就要占用一个 I/O 地址,该地址称为是 I/O 端口地址, 8086 提供给 I/O 地址的内存是 64KB,一个端口可以占用多个地址,但某个地址在被端口使用后,不可以再成为其他端口的地址。
某些微处理器采用统一的地址空间对存储器和 I/O 端口寻址, 即存储器和 I/O 端口进行统一的地址编码,一个地址要么对应于存储单元,要么对应于端口寄存器,读写控制信号用来区分 CPU 进行读/写操作(采用统一地址空间的微处理器没有 引脚区分存储器地址和 I/O 地址)。在这种方式下,对存储器和 I/O 端口的存取指令是一样的。(统一的地址空间意味着将存储器地址与 I/O 端口地址混合起来,用连续的地址编码来管理,具体是存储器地址还是 I/O 地址需要看地址对应的端口来决定)。如MCS-51。
优点:I/O 驱动程序编写方便,灵活(比如地址的寻址既可以对 I/O 端口寻址,又可以对存储器寻址)。缺点:浪费了存储器地址空间(给 I/O 端口的地址越多,给存储器的地址就越少),无专用的 I/O 端口指令。
但大多数微处理器则是采用两个独立的地址空间,即存储器地址空间和 I/O 地址空间。此时某存储单元和 I/O 端口可能对应相同的地址值。那么如何区分 CPU 是存取存储单元还是 I/O 端口?采用 引脚 来区分。在这种方式下,对存储器和对 I/O 端口读写指令是不同的。如 8086。
优点:节约了存储器地址空间。缺点:指令记忆复杂(既需要记忆存储器地址指令、有需要记忆 I/O 地址指令),I/O 驱动程序设计不灵活(寻址方式没有存储器多)。
寻址(在后面会详细介绍):
对于汇编程序有两种常见的寻址方式,一种是直接寻址,一种是间接寻址。
微处理器的内部结构
了解微处理器的工作原理,逐渐掌握汇编语言设计的编程模型。

不管任何型号的 CPU ,其内部都应该由上面这四个部件组成。

在控制器中,器件的运行顺序是 程序计数器 → 指令寄存器 → 指令译码器 → 控制逻辑部件 → 程序计数器…
当下一条指令的地址为 2 个字节的时候,程序计数器就读取两个字节,如果指令地址为 3 个字节,那么就读取三个字节。
在上面的过程中,控制器件每次只能取一条指令放入寄存器中,指令译码后执行,执行代码后才能再取下一条指令,因此这种方式的效率是十分低的,而 8086 CPU 可以并行执行指令读取和指令执行的过程,效率更高。
微处理器的功能结构
8086 CPU 的高效之处在于具有多个指令寄存器,可以存放多个指令,在前一个指令执行后,CPU 会补充新的指令进来,同时准备下一条指令的执行。

图中展示了微处理器的功能结构,其中 IP 表示的就是指令指针(PC),即程序计数器。
BIU 部分的理解
BIU 负责从外部存储器中取指令,并将取回的指令放入队列中。
1. BIU(总线接口单元)的理解
- CS, DS, SS, ES:这些寄存器是段寄存器,分别代表代码段(Code Segment)、数据段(Data Segment)、堆栈段(Stack Segment)和附加段(Extra Segment)。段寄存器存储段地址,用于定义内存中某个特定段的起始地址。
- IP:指令指针寄存器(Instruction Pointer),它保存的是当前指令在代码段中的偏移量,即段内偏移地址。
2. 段地址和段内地址的关系
- 在 8086/8088 处理器中,内存地址由段地址和段内地址(偏移量)共同确定。段地址存储在段寄存器中,而段内地址通常由 IP 或其他寄存器提供。
- 物理地址的计算方式是将段地址左移4位(即乘以16),然后加上段内地址。计算公式如下:
这种方法使得一个段可以表示 64KB 的内存范围。
3. BIU 与地址产生和总线控制的关系
- 地址产生与总线控制:地址产生单元通过读取段寄存器和指令指针(IP)寄存器的值,生成物理地址并与总线控制逻辑合作,负责内存和I/O的读取和写入操作。
- BIU 在生成物理地址后,通过总线接口将这些地址和数据传输到系统总线,然后访问相应的内存或I/O设备。
EU 部分的理解
EU 主要负责从指令队列中获取指令,并对该指令加以执行。

在上述过程中,总线一直处于忙碌状态,也就意味着效率大大的提高。
教材内容

微处理器的寄存器组织
共有 14 个 16 位的寄存器,通用寄存器有 8 个(数据寄存器 4 个,指针寄存器 2 个,变址寄存器 2 个),段寄存器 4 个,控制寄存器 2 个。
通用寄存器
数据寄存器
数据寄存器有 4 个 —— AX(又称为累加器), BX(又称基址寄存器), CX(又称计数寄存器), DX(又称数据寄存器)。
因此在使用目的操作数时尽可能多的使用 AX;在使用段内地址时尽量使用 BX;在使用循环操作时尽量使用 CX,这样计算机的效率更高;
指针寄存器和变址寄存器
地址指针寄存器 → BP;
与 BX 基址寄存器不同,由 BP 提供的段内偏移地址默认在堆栈段(SS),而由 BX 提供的段内偏移地址默认在数据段。
代码:如何将 BP 提供的段内偏移地址更改为数据段?
堆栈指针寄存器 → SP;
默认在堆栈段。
源变址寄存器 → SI;
默认是 DS 段的。
目的变址寄存器 → DI .
默认是 DS 段的。
那么源变址寄存器和目的变址寄存器的用途是什么呢?
在计算机内部对字符串进行操作时,可以直接利用数据寄存器进行操作,但是为了提高效率,汇编语言中直接开辟了一块专门用于对字符串进行操作的指令,通过变址寄存器来实现。

以 数据搬家 为例,要想利用字符串操作的指令,首先数据必须位于数据段(DS),其次,字符串的偏移地址必须保存在 SI 内;目的串必须定义在附加数据段(ES),字符串的偏移地址必须由 DI 寄存。这就是 SI 和 DI 两个寄存器名字中 “源” 和 “目的” 的由来。
那么名字中的 “变” 体现在什么方面呢?
在计算机执行
MOVSB
(按字节移动)指令后,SI 和 DI 的地址会自动的加 1 或减 1,也就是上移或下移,以实现数据的移动。其中 MOVS
表示的是对字符串进行移动操作,B
表示对字节,W
表示对字。段寄存器
段寄存器的大小都是 64KB。
CS → 代码段寄存器(无需用户初始化,用户无权操作,由操作系统完成)
换句话说, CS 不能作为目的操作数,不能对 CS 进行写操作。
DS → 数据段寄存器(需要用户初始化地址)
ES → 附加数据段寄存器(需要用户初始化地址)
SS → 堆栈段寄存器(需要用户初始化地址)
控制寄存器
包括指令指针寄存器(IP)和微处理器状态字寄存器(PSW)。
指令指针寄存器等同于程序计数器(PC),用于保存下一条即将要执行指令的段内偏移地址。
PSW 共有 6 个反映 ALU 结果状态的标志位,状态标志位—— ZF, AF, SF, OF, PF, CF,其中除了 AF 以外其他都是条件分支程序设计当中的条件之一。

PSW 中还有 3 个控制标志位,用于控制 cpu 运行状态,分别是 —— DF(方向控制),IF (中断控制),TF(陷阱标志)。


老师上课提出的问题:如何设计代码让 TF 置 1?
堆栈
由先进后出原则组织的一段存储器区域。堆栈的区域是程序员在编程的时候根据需求定义的。
放入数据的过程为
PUSH
(助记符),拿出数据的过程称为 POP
(助记符)。如 PUSH DX
, POP AX
.对于 8086 而言,堆栈必须按字操作,且必须对寄存器的内容 或 内存单元的内容进行操作,例如
PUSH 2000H
就是错误的。同时还有两个注意点(现在不一定理解,等学到后面就明白了):
- 对于代码段寄存器 CS ,只能够通过 PUSH 入栈,不能够通过 POP 出栈。
也就是说,
PUSH CS
是正确的, POP CS
是错误的。- 控制标志字 TF 指令只能够通过堆栈操作来修改(通过 POPF 指令直接将堆栈区最靠前的一个字弹入标志字寄存器中 PSW 中),而 DF 和 IF 都有专门的指令修改(STD 设置 DF 为 1,STI 设置 IF 为 1,CLD 清除 DF,CLI 清除 IF)。

在堆栈区中,下面的地址表示的是高地址,上面的地址表示的是低地址。
在上图所示的操作中,BL 所在的位置就是栈顶,AH 所在的位置是栈底,SP 表示的是栈区的指针,在堆栈区为空时,指向 (栈底 +1)的位置。可以看到,AX 是先进入栈区的,BX 是后进入栈区的,因此使用
POP
指令将数据弹出堆栈区时,如果要恢复原来的数据,必须先弹出 BX,再弹出 AX(当然,如果要实现的操作是交换 AX 和 BX 中的数据那么就先弹出 AX, 再弹出 BX)。每次执行
PUSH
指令时,栈区指针 SP 会自动进行“减2”操作,且是指针先减 1,压入一个字节,再减 1 再压入一个字节;每次执行
POP
指令时,SP 会自动进行“增2”操作,且是先移出一个字节,指针再加 1,然后再移出一个字节,指针再加 1。其实很容易理解,就是只有指针当前指向的位置才能够进行压入和移出操作。
PUSH 和 POP 指令一般情况下都需要相互配对。
栈顶(SS)是固定的,栈底(SP)是变化的。
栈顶为低地址,栈底为高地址。
如果在堆栈的过程中,指针超出堆栈的空间,那么就会发生栈的溢出。溢出后计算机就会死机。因此在设计堆栈的区域的时候,要根据实际需求来,并且留出 30% 的富余量。
存储器地址空间与数据存放格式
地址空间
共 20 根地址线
给存储器编址的地址线有 20 根,为 ;
给 I/O 编址的地址线有 16 根,为 ;
在 IBM PC 机中,给 I/O 分配地址的地址线有 10 根,为 ,因为在显示应用中给 I/O 的地址往往用不了 的内存。1 KB 的内存中,前半 k 的部分分配给主板上的 I/O 作地址,后半 k 分配给插件板上的 I/O 作地址。

数据存放格式
分为字节型、字型、双字型,其中前两种比较常用,双字型的数据并不常用。
字节型数据: 存放在 DS 段,用于定义字节型常量
字节型数据伪指令

上图中右侧的地址表每个格子表示一个字节的数据,因此如果使用 DB 伪指令来定义数据,那么每个数据只会占用一个格子。
伪指令的含义
在微机原理和汇编语言编程中,伪指令(也称为伪操作或汇编伪指令)是由汇编器(assembler)解释和处理的指令,但这些指令在实际的机器代码中并不存在。换句话说,伪指令并不会被直接翻译成机器指令(即二进制代码),而是指导汇编器如何处理源代码的某些方面。伪指令主要用于程序组织、内存分配、定义常量等操作。
有关伪指令的更多内容在第四章会学习到。
字型数据:存放在 DS 段,用于定义字型常量

如果使用 DW 来定义数据,那么数据就占用一个字,也就是两个字节,占两个格子。
数据对准与未对准:




数据对准与未对准是针对字型数据而言的。


当字型数据的首地址(低 8 位数据所对应的地址)为偶地址时,称该地址是对准的,否则是未对准的。对于对准的地址而言,读写数据操作的速度更快,只需要花费一个总线周期的时间。而对于未对准的数据,需要花费两个总线周期的时间。具体为什么是这样在后面介绍 时会解释,当然也可以【Ctrl+F】搜索 “BHE” 找到该内容。
存储器的分段与物理地址的形成
为什么要进行分段?
在 8086 内部,用于外部存储器寻址的地址线是 20 条,最大能够表示的存储空间为 ,但是用于存储器寻址的寄存器(如 BX BP SI DI等)的大小却都是 16 位的,只能为存储器提供 ,也就是 64 KB 的寻址空间。为了能将 1 MB 的空间都能够寻址完,需要将存储器分成许许多多的逻辑段进行管理,每个逻辑段的最大长度不能超过 64 KB。
如何分段?
段地址的最大空间为 64 KB,每个逻辑段的起始地址必须能够被 16 整除,因为每个段的第一个偏移地址必须是从 0 开始的(后面会解释)。
物理地址与段地址和段内偏移地址之间的关系为:

其中段地址和段内偏移地址都是 16 位的,通常使用
3434H: 2046H
这样的形式表示物理地址,冒号左侧为段地址,冒号右侧为段内偏移地址,这种表示下的地址称为是逻辑地址,在实际调试代码的时候看到的地址都是逻辑地址。下面解释为什么物理地址是通过这个公式进行表示的:
物理地址是段地址左移 4 位后加上段内偏移地址组成的,对于不同的段而言,段内偏移地址可能是相同的,但是段地址肯定是不同的(如果段地址相同,不同段之间就无法区分了),也就是说物理地址是唯一的。
那为什么是段地址乘以 16 位(或者说段地址左移 4 位)呢?
其实只要能够唯一的表示物理地址,左移几位都行。由于段内偏移地址最大为 16 位二进制数,而物理地址最大为 20 位二进制数,因此通过将段地址左移四位后,段地址就是 20 位的了,再加上 16 位的段内偏移地址,最后的结果可以用 20 位的二进制数表示。也就是说,8086 可以通过 20 根地址线访问到相应的物理地址。
如下图所示,物理地址为蓝色的部分,由 5 位 16 进制数表示,高四位表示的是段地址,低四位表示的是段内偏移地址。由于段内的偏移地址必须从 0 开始,因此段起始地址的低 4 位一定是 0,低 4 位为 0 就意味着它能被 16 整除。

在理论上,不同逻辑段之间会相互重叠,因为段地址的起始地址只需要能够被 16 整除即可,图中的 0 段和 1 段之间就发生了重叠,重叠意味着在给不同的段赋值的时候数据会相互覆盖,因此在实际应用当中,逻辑段一般是不会发生重叠的。用户在编写程序的时候,首先需要自己对数据段、堆栈段、代码段等段进行定义(定义他们的大小),然后才在代码段中写程序。编写的程序经过汇编语言汇编后,系统会依次形成所需要使用的逻辑段,下一个逻辑段会在上一个逻辑段的结束地址后寻找可以被 16 整除的地址,因此地址是不会发生重叠的。
8086 是如何将段地址与段内偏移地址合成为物理地址的:

以
MOV [BX], AX;
操作为例,首先告诉 BIU 要进行传输器写操作了,取指令的操作暂停,帮 EU 完成存储器读写操作,此时 BIU 会将 CS 段地址的值传给 BIU 内部的 ALU,在 ALU 内部段地址会乘以 16 ;然后 EU 通过内部地址总线将 BX 的值传输给 BIU 的 IP,此时 IP 存放的就是段内偏移地址,将 IP 中的段内偏移地址也传给 ALU与 CS 的段地址相加,就能得到 AX → BX 后数据存放的物理地址。最后 EU 将 AX 内的数据放入数据总线,同时将物理地址放入地址总线,再给外部传输器发送一条写命令,即可完成数据传送的操作。要注意,在上面的操作中使用到的 CS 段和 IP 指针寄存器中的值都是用户无法更改的,它们的值只能由系统计算得出。
各类指令的地址信息:

如上图所示,在进行取指令代码操作时,段内偏移地址必须由 IP 提供,且必须在 CS 段执行;
在进行堆栈操作时,段内偏移地址必须由 SP 提供,且必须在 SS 段执行;
在进行字符串操作源地址时,段内偏移地址必须由 SI 提供,进行字符串操作目的地址时,段内偏移地址必须由 DI 提供;
8086 指令系统及程序设计(第三章与第四章部分内容)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 汇编语言指令
- 冒泡排序法
- 8086 系统指令
- 宏指令
- 信息保护、恢复
- 子程序
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
汇编语言指令
名词解释:
指令系统: cpu 能够识别的所有指令的集合。
机器语言:将指令代码语言转换成机器语言,指令繁琐难记。
机器语言程序:由机器语言编写的程序称为是机器语言程序。机器语言程序是 CPU 能够直接识别的程序。
汇编语言:符号化语言。汇编语言会在编译时转换为机器语言。
汇编语言程序:由汇编语言编写的程序称为是汇编语言程序。
汇编:将汇编语言程序翻译为机器语言程序 的过程称为汇编。

汇编程序及文件
用于汇编的程序为 MASM.exe ,使用 5.0 以上的版本。
各个汇编文件间的关系:
- 编写汇编语言源代码,并保存在
.asm
文件中,此时 asm 中的文件是 cpu 不能直接识别的。
- 使用汇编器将
.asm
文件编译为.obj
目标码文件,目标码文件中的代码是 cpu 能够识别的机器语言代码。
- 由于 obj 文件中的地址是浮动的,代码是不连续的,cpu 也不能直接执行。因此要使用链接器
LINK.exe
将一个或多个.obj
文件链接成可执行文件(.exe
)。
- 如果需要调试,可以使用
DEBUG.exe
来加载和调试生成的 exe 文件。
汇编语言程序中语句的种类
- 指令语句。能够汇编成指令代码的语句。
- 伪指令语句。CPU不能直接执行的语句,不能汇编成二进制指令代码。是汇编器要明白的语句。
- 宏指令语句。是8086本身没有的指令语句,是用户宏定义伪指令定义的一条新的语句。类似于 C 中的函数。
汇编语言中的语句组成
在汇编语言中,为了方便寻找数据或是指令的地址,可以使用变量名和标号来方便程序员进行索引、跳转。
变量名和标号名都可以用于记录语句信息(包括地址),区别是变量名只能在定义变量时使用,方便后续寻找变量所在的地址,而标号名可以是存放除变量定义外的任意语句的相关信息,可用于地址跳转。
变量名和标号名的定义方式如下:
【变量名 助记符 操作数】:如
DAT1 DB 12H, -12, 12
。定义了三个以字节为单位存放的常数 12H 和 -12、12,他们在数据段中连续排列,首地址为 12 H 所对应的地址,首地址的相关信息(如段地址和偏移地址)会存放在 DAT1 这个变量名中。 【标号名: 助记符 操作数】:如
NEXT: MOV AX, BX
。标号名可以储存语句所在行的地址,方便程序员后续调用此地址的内容。注意标号名后面有一个冒号。用于存放标号所在行的地址信息。如下图中,定义了 NEXT
标号名(名字可以随便取),表示 MOV AX, BX
这一行语句,当使用 JZ NEXT
指令时,就会跳转到标号名所在行再次顺序执行代码。
无论是变量名还是标号名,都是名称,需要以字母开头。
汇编语言语句的组成、常数与表达式(课本内容)


标号的介绍(课本内容)

如果要获得标号的段地址,可以使用 SEG,如果要获得其偏移地址,可以使用 OFFSET。
变量的介绍(课本内容)



属性操作符和 PTR


LENGTH:
LENGTH 只在变量定义时有 DUP 出现才有效,其他情况下都是 1 .

TYPE 和 SIZE 的区别:
TYPE 用于返回变量或数组中每个元素的大小,以字节为单位。
SIZE 用于整个变量的大小或数组所占的总字节数,SIZE = LENGTH * TYPE。
在上述代码中,
TYPE myArray
返回的是 1, SIZE myArray
返回的是 3,因为 myArray 数组有三个字节。如果 myArray 是 DW 定义的,那么 TYPE 就是 2;如果 myArray 是 DD 定义的,那么 TYPE 就是 4 。
PTR 指令可以临时改变变量的类型,通常用于这样的场景中:
MOV AX, WORD PTR DAT1
,其中 DAT1 是字节型的变量;或者,也可以将目的操作数进行变量转换 MOV BYTE PTR [BX], 01H
。否则就会发生变量不明确的问题。$ 运算符
MASM.exe 软件中还有一个特殊的操作符
$
,作为位置计数器。例: DAT1 DW $
就定义了一个 16 位的变量 DAT1,存放的是 DAT1 当前的地址。
老师上课的时候说 DD,DW定义字符串只能有2个,DB 定义字符串不超过64K,因为段地址最长为 64K,段的寻址只能寻 64K。后面的字符串长度可以理解,前面的“ DD DW 定义字符串只能有两个”没有弄明白。
8086 指令分类


数据与转移地址的寻址方式
寻址方式指的是求操作数所在地或所在存储器单元地址的方式。是直接获得操作数,还是通过地址来寻找操作数,这就是本节内容所要探讨的。
数据的寻址方式
寻址方式的图示
后面的几种寻址方式结合图示来看会更加清晰明了,印象也会更深刻。

寻址需要遵循的几个语法规则
加上方括号的量一般都是指存储单元,如 [BX], [3000H] 等。
- 立即数只能做源操作数;
- 源操作数和目的操作数的类型要明确;
- 源操作数和目的操作数的位数要一致,如 MOV BX, AL 就是非法的;
但请注意,MOV AL, [BX+5]
或是MOV AX, [BX+5]
这样的指令是合法的,因为把 “BX+5”地址的数据放入 AL 中时会自动取 8 位数据,同理,把 “BX+5” 地址的数据放入 AX 中时会自动取 16 位数据。
- DS/ES/SS 这三个段寄存器做目的操作数时源操作数不能为立即数;
- CS 段寄存器和 IP 指针不能做目的操作数;
- 两存储器单元之间不能直接操作;
- 指令语句中不允许两个变量参与运算。
在做题过程中总结出的几点规则补充:
- 对于 BX BP SI DI 这四个寄存器,在相对寻址时,只能是 BX/BP 与 SI/DI 相互搭配,不能将 BX 与 BP 搭配, SI 与 DI 搭配,如
MOV CX, [BX+BP]
指令就是错误的。
MOV DX, SS
指令是错误的,如果要取出 SS 的值,可以通过PUSH SS
,POP DX
来得到。
关于第三点“位数”这个,如果源操作数和目的操作数的位数不一致,需要使用 PTR 操作符来使操作符合条件。
一般来说,当源操作数或目的操作数中有一者为变址寄存器 SI 或 DI 的时候需要使用 PTR 操作符,因为这两个寄存器的位数不确定。如
MOV BYTE PTR [SI], AL
。立即寻址
什么是立即数:

什么是立即寻址:

如果要将立即数转移到段寄存器中,需要使用通用寄存器做跳板,先将立即数转移给通用寄存器,然后再移动到段寄存器中。
寄存器寻址

在寄存器寻址时,有几个注意点
- 源操作数与目的操作数的类型要一致。如
MOV AX, BX
不能写成MOV AX, BL
- 源操作数与目的操作数至少有一方类型要明确。如
MOV WORD PTR [0200H], 53H
,要有类型转换符。
- 当使用段寄存器 DS, ES, SS 作为目的操作数时,源操作数不能是立即数,立即数要先传递到通用寄存器中。
存储器寻址
要寻找的操作数在存储器的某单元中,因此要通过寻址来找到存放操作数的存储器地址,存储器寻址有以下五种方式。
直接寻址。指令中直接写出操作数单元的地址。


在直接寻址的过程中,要注意的是 两存储器单元之间是不能直接操作,操作包括寻址、相加或是其他运算。例如
MOV DAT1, DAT2
就是错误的,需要先将其移动到寄存器中,再进行下一步操作。
寄存器间接寻址。[BX] | [SI] | [DI]

注意:
MOV [AX], [SI]
这样的语句是错误的,需要先将其中一个地址取出放入寄存器里,再移到另一个寄存器中。寄存器相对寻址。【BX, BP, SI, DI + 8/16 位 disp(相对偏移量)】

特别注意, MOV DX, [BX+BP] 或 MOV DX, [SI+DI] 这两个指令是错误的,因为寄存器相对寻址时,只能是 BX BP 与 SI DI 相互搭配,不能将 BX 与 BP、SI 与 DI 组合。
示例:

寄存器间接寻址是寄存器相对寻址的特殊情况。
【清华大学出版的《微机原理与接口技术》(ISBN:978-7-302-24540-7)中没有“寄存器相对寻址”,它将寄存器相对寻址拆分为了“基址寻址”和“变址寻址”;同时在这本书中,寄存器间接寻址四个寄存器(BX, BP, SI, DI)都可以使用。】
基址【BX BP】变址【SI, DI】寻址 (格式: [BX][SI])

上课老师举的例子

基址,变址且相对寻址 {【BX, BP】, 【SI, DI】+ 8/16位 disp}

隐含寻址

转移地址的寻址方式
无条件转移指令,如 JMP 既可以实现段内转移,也可以实现段间转移;
而在 8086 系统中,所有的条件转移指令都只能在段内转移,且转移的范围为 -128 ~ 127 之间。
如果在程序设计时,使用条件转移指令,但转移的范围超出了上面提到的范围,那么需要使用 JUMP 语句搭桥。
所有的条件转移指令都采用段内直接寻址(或者叫做段内相对寻址)。
JMP 指令的介绍及其与 IP 的关系
JMP
指令和 IP
(指令指针)之间的关系是非常密切的。在8086汇编语言中,JMP
指令用于改变程序的执行顺序,直接修改 IP
的值,使程序跳转到一个新的地址继续执行。JMP
指令的基本工作原理:IP
寄存器:
IP
寄存器存储了当前正在执行的指令的地址(相对于当前代码段 CS
的偏移)。每次执行完一条指令后,
IP
会自动递增,指向下一条指令的地址。JMP
指令:
JMP
是“跳转”指令,它直接修改 IP
的值,使得程序不会顺序执行下一条指令,而是跳转到由 JMP
指令指定的新地址。通过
JMP
指令,程序的执行流可以跳转到代码的其他部分。JMP
和 IP
的关系:- 修改
IP
:JMP
指令的执行实际上就是把IP
的值设置为跳转目标地址的过程。无论是短跳、近跳还是远跳,JMP
都是通过改变IP
的值来实现跳转的。
- 目标地址:
JMP
指令通过提供一个目标地址或偏移量来设定新的IP
值。这个目标地址可以是绝对地址,也可以是相对于当前IP
的一个偏移量。
段内直接寻址(相对寻址)

有关 JMP 指令 IP 值的计算


例题一
题目:
8086 CPU 执行
JZ L1
指令时 IP=0100H, 相对偏移量为 0FDH,指令执行后 (IP)=?求解过程:
求解的式子 IP = IP + 2 + 0FD H = 00FF H。
由于
JZ L1
指令本身占 2 个字节的内容,因此执行完 JZ L1
指令后,IP = IP+2,进行跳转后,相对偏移量是根据新的 IP 值来进行计算的,注意这个相对偏移量是一个负数。
有以下两点需要注意:
- 计算机内部都是使用补码来存储数的。
- 8 位的地址
FD H
可以通过符号位扩展得到 16 位的地址FFFDH
。
例题二
题目

求解过程

段内间接寻址

例题:假设有一段代码,可以实现的功能如下:当 AL = 1时,跳转到 L1; 当 AL = 2 时,跳转到 L2;当 AL = 3 时,跳转到 L3,该如何实现呢?
8086 系统的各种指令
数据传送类指令

两个注意点
① 除 SAHF、 POPF 指令外,其余传送类指令 CPU 执行后,对 6 个状态位标志均无影响。
② 要遵循前面的 6 个语法规则
- 立即数只能做源操作数;
- 源操作数和目的操作数的类型要明确;
- 源操作数和目的操作数的类型要一致;
- DS/ES/SS 这三个段寄存器做目的操作数时源操作数不能为立即数;
- CS 段寄存器和 IP 指针不能做目的操作数;
- 两存储器单元之间不能直接操作;
- 指令语句中不允许两个变量参与运算。
通用传送类指令(MOV)

取有效地址指令(LEA)

在进行 LEA 指令的时候,目的操作数最好是放在 BX, BP, SI, DI 中,放在AX BX里面也可以,但是这样就不能间接寻址相对寻址,源操作数必须是五种存储器寻址方式之一指明的单元。

取地址指针指令(老师原话:用的不多,但也要会)
LDS 和 LES 可以将某个变量或标号的段地址和段内偏移地址都取出来,由于段地址和段内偏移地址都是以字为单位的,因此将低 16 位(段内偏移地址)放入你指定的寄存器中,高 16 位(段地址)依据 LDS 和 LES 指令不同存放到寄存器 DS 或 ES 中。更具体的介绍如下:

老师上课举的例子
假设有一个变量 x (假设 x 值为 0FFH)的地址为 A: B,A 表示变量 x 的段地址,B 表示的是变量 x 的段内偏移地址。
现在有以下的指令:
标志传送指令(LAHF, SAHF)

由于标志传送指令 LAHF 和 SAHF 只能够处理低 8 位(0~7位)的数据,因此如果要处理高 8 位的 flag 还需要利用到堆栈(PUSH/POP)。

数据交换指令(XCHG)

源操作数与目的操作数需要满足:
- 均不能是立即数;
- 不能同时为存储单元;
- 不能使用段寄存器。
字节转换指令(XLAT)
寻址方式为隐含寻址。
教材中的介绍


例题:

首先使用 TABLE 定义了一系列字节数据 30H,31H …,他们在数据表中是顺序存放的。第一个字节数据的首地址即为变量名 TABLE 的地址,当使用 LEA 指令时取出 TABLE 的段内偏移地址存放到 BX 中。
AL 为 1 时, XLAT 指令可以寻址到段内偏移地址为 [BX+1] 的值,即 31 H;
AL 为 4 时,XLAT 指令可以寻址到段内偏移地址为 [BX+4] 的值,即 34 H;
堆栈操作指令(PUSH, POP, PUSHF, POPF)
教材中关于堆栈的介绍



需要特别注意的是 PUSH 指令可以 PUSH CS 段的偏移地址,但是 POP 指令不能弹出 CS 段的地址,如下面的例题所示:

如果需要弹出先前
PUSH CS
指令压入堆栈段的内容,可以通过通用寄存器来实现:有关 PUSH 和 POP 指令的示例:

算数运算类指令
① CPU 只要参加运算,其结果就会影响状态标志。② 段寄存器不能参与运算。上述规则不仅适合于算术运算,也适合于逻辑运算。
加法指令

ADD 加法指令为 不进位加法指令,ADC 为进位加法指令。

INC DST
指令为自增指令,将 DST 的内容加一后赋值给 DST,其中 DST 的内容不能是立即数,INC
指令只有五个标志位,不包含 CF 进位标志位。书上例题

减法指令

比较指令

比较指令相当于进行了减法运算但不储存结果,只记录过程中符号位的变化,方便后续进行相关的条件语句。
8086 指令系统中涉及到的条件语句指令:
求负(取负)指令

将源操作数取负后重新设置状态标志位。
例题:
题目:
在存储器的 BUFFER,有一个 16 位的带符号数,求该数的绝对值,并将结果放回原处。
求解过程:
① 分析题目,提出算法
求一个数的绝对值其实可以使用一个分段的数学表达式来表示,。
因此需要先判断 x 与 0 的大小,如果小于 0 需要进行取负的操作。
② 绘制流程图

③ 编写程序
乘法指令

执行乘法指令后,只影响 CF 和 OF 两个标志位,其他标志位的结果没定义。
除法指令

例题:
题目:

求解过程:

符号扩展指令

BCD 数调整指令


加法调整指令:

减法调整指令:

乘法调整指令:

除法调整指令:

逻辑运算类指令

逻辑与:

逻辑或:

逻辑异或:

逻辑非:

TEST 指令:

移位类指令



条件转移指令
无条件转移指令 JMP 既可以实现段内转移,又可以实现段间转移; 有条件转移指令只能实现段内转移。
无条件转移指令(JMP)

有条件转移指令



循环指令

LOOP 指令


LOOPZ 指令
LOOPNZ 指令
JCXZ 指令

字符串操作指令






在执行了上述的操作指令后, SI 和 DI 的值都会发生改变,但具体是往增加的方向改变还是减小的方向改变需要看 DF 的值。若 DF = 0 ,则往增加的方向改变,若 DF =1,则往减小的方向改变。具体增减多少,要看调用的类型。
串操作指令的左边可以增加重复前缀,具体重复的次数存放在 CX 中。

输入输出指令(IN/OUT)
在前面,我们采用 MOV 指令来访问存储器设备,但是对于 IO 端口而言,我们不能使用 MOV 指令进行访问。IO 端口有专门的输入输出指令。
端口输入指令(IN)

在使用间接寻址的过程中,如果采用的是 DX ,则不需要加上中括号(因为 DX 在 IN 和 OUT 指令中直接代表的是端口地址);
如果使用的是 SI, DI, BX,那么需要加上中括号。
这意味着采用间接寻址时, DX 不需要做类型转换,但是 SI DI BX 需要,如 IN AL, DX
端口输出指令(OUT)

IN/OUT 指令总结及注意点

CPU 执行一条指令所花费的时间称为总线周期,对于端口输入操作而言,需要花费 4 个总线周期,对于端口输出操作,多了一个等待周期,需要花费 5 个总线周期。
关于 IN/OUT 指令的注意点:

其他指令
标志位处理指令

处理器控制指令

宏指令

定义:

宏调用与宏展开:

宏指令中的标号与变量:

使用 LOCAL 的示例:

在新程序中使用 DATAMOV 宏指令时, NEXT 的地址就会自动设置为 ??0000 ??0001,这样就避免了 调用 DATAMOV 两次以上时,NEXT 会重叠。
宏指令与子程序的区别
- 定义和使用方式:
- 宏指令: 宏指令是在汇编代码中定义的一段代码块,可以在程序中多次调用。每次调用时,宏指令会被展开为相应的汇编代码。也就是说,宏指令的代码会被直接插入到调用宏指令的位置。
- 子程序: 子程序是一段可以在程序中调用的代码段,但与宏指令不同的是,子程序在调用时不会展开代码,而是通过跳转的方式执行代码,然后返回调用处。
- 性能:
- 宏指令: 由于每次调用宏指令时,代码都会被展开,因此在程序中多次调用宏指令会增加代码的长度。这在代码规模较大时可能会导致程序的可执行文件变大。
- 子程序: 子程序的代码仅在内存中存在一份,不管调用多少次都只占用这一份代码空间,因此可以有效减少可执行文件的大小。
- 效率:
- 宏指令: 宏指令在编译时展开,减少了程序运行时的跳转操作,因此在执行速度上可能比子程序快。
- 子程序: 子程序调用时会有跳转和返回操作,因此可能稍微降低程序执行的效率。
- 灵活性:
- 宏指令: 宏指令比较灵活,可以通过参数生成不同的代码块,但它不能像子程序那样使用局部变量和返回值。
- 子程序: 子程序支持使用局部变量、参数传递和返回值,适合处理更复杂的逻辑和数据操作。
子程序
将 8086 中完成某个特定功能的外挂程序称为子程序,和 C++ 的函数一样。
子程序又称为是一个过程。
子程序的定义代码如下:
<子程序名> PROC [类型]
子程序名和标号名和变量名一样,需要以字母开头且有实际意义。类型可以是 NEAR ,也可以是 FAR 。如果类型是 NEAR ,类型名可以缺省。子程序可以通过 CALL 来调用(后面会细说),如果 CALL 指令与子程序不在一个段内,就需要显式指明是 FAR 类型的。
子程序内部的代码称为子程序体,或是功能体。在子程序段结束后,要在末尾加上
RET
(return) 语句,这样程序地址就会返回到调用语句的下面一段代码处,继续源程序的运行。如果是段间调用的代码,理论上来说最后的返回语句应该是 RETF
,但是由于在子程序定义的时候就已经声明了类型,因此也可以用 RET
来替代。在定义完一个子程序体后,RET 的作用是告诉调用的指令要返回了,但是在 RET 的后面还要加上一个 <子程序名> ENDP
来在形式上结束这个程序段,有始有终,此处的子程序名与 PROC
前面的要相同。子程序名一旦定义,就具有了三个属性——① 段地址属性; ② 段内偏移地址属性(EA);③ 类型的属性。
在实际的设计中,一般都是采用程序模块化的方式来编写程序的,这样设计的程序如果要加入新的功能或模块 ,只需要另外设计一个子程序,再将 CALL 语句加入源程序就可以了。
子程序的调用与返回
子程序调用的代码:
CALL <子程序名>
,程序调用的过程中,堆栈段自动进行如下的操作:
段间子程序调用:

虽然程序调用过程中会自动地进行堆栈的操作,但是,如果在子程序内部有进行对战的操作,一定要记住 PUSH 和 POP 两个要配上对,否则会破坏 SP 指针,使得程序无法正常返回。
课本中关于子程序调用的说明:

课本中关于子程序返回的说明:

信息的保护与恢复
信息的保护与恢复是通过堆栈来完成的。
信息的保护与恢复既可以在子程序外进行,也可以在子程序内进行,但推荐在子程序内进行。
不管如何,一定要记住先入后出原则,PUSH 和 POP 要配上对。
主程序与子程序之间参数的传递
有三种方式:
- 寄存器传递(寄存器只有 14 个,传递的参数有限);
- 存储器单元传递(要在数据段定义存储器单元);
- 堆栈方式传递。
子程序的说明文档
子程序的说明文档是为了让别人能够理解子程序的功能,方便别人调用。
需要包含的内容:
- 子程序名;
- 子程序的功能;
- 子程序的入口参数;
- 子程序的出口参数;
- 用到的寄存器(避免用户之前使用了子程序中相同的寄存器,造成原数据的丢失);
- 调用示例。
子程序的嵌套
子程序理论上是可以无限嵌套的,但是实际上却又受到堆栈区域大小的影响,不建议设置太多的嵌套,不利于调试。
子程序的嵌套是自上而下的,但是调试是自下而上的。
可再入子程序

程序的中断与调用
这个部分和第八章的牵扯比较多,因此将这一部分的内容全部都放到第八章的笔记中。
汇编语言程序设计(第四章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 伪指令
- 条件分支
- 冒泡排序法
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
程序设计基础(伪指令)
程序设计的步骤

段定义伪指令

微机原理中表示结尾的标志:
- END 。用于标志整个程序的结束。它通常用于程序的最后一行,表示汇编程序的结束。
- ENDS。S 表示 SEGMENT,用于标志段定义的结束。
- ENDP。P 表示 PROGRAM,用于标志子程序定义的结束。
组合类型的介绍(书上没有,但南理工要考):

STACK 类型:

系统会自动创建一个 256 B 大小的空间用于堆栈操作,如果想要插手这个过程,可以使用下面的方式:

在堆栈段定义过程中如果使用了 TOP LABEL WORD 手动设置了地址,那么在进行段初始化的过程中需要手动初始化堆栈段(如图中红框部分所示),更改 SP 的位置,改变堆栈段的大小。如果没有使用 TOP ,或是不打算更改 SP,那么红框部分可以省去,因为系统会自动进行初始化操作。
LABEL 伪指令

ASSUME 伪指令(代码段)

在段定义之后,就要进行 ASSUME 操作,要注意的是,如果 ASSUME 指令无法在一行内完成,换行时,仍要使用新的 ASSUME 开头。

END 伪指令

程序结束时最终需要些写的代码如下:

EQU 伪指令和 “=”伪指令

GPT 对 EQU 伪指令的解释
在微机原理中的汇编语言中,
EQU
和 =
是两种常用的伪指令,用于定义常量或符号名称。虽然它们在功能上有相似之处,但有一些关键的区别。EQU
指令
- 作用:
EQU
指令用于为一个符号赋予一个固定的值。该值在整个程序中是恒定的,不能被重新定义或修改。
- 类比:在 C++ 中,
EQU
指令的作用可以类比为const int a = 3;
。EQU
定义的符号是不可变的,就像const
修饰的变量在 C++ 中不能被修改一样。
上述代码中,
MAXLEN
被定义为常量 100,在程序的其他地方都可以使用 MAXLEN
来表示 100,但不能重新赋值。=
指令
- 作用:
=
伪指令也用于给符号赋值,但与EQU
不同的是,使用=
定义的符号可以在程序中被多次重新定义。
- 类比:在 C++ 中,
=
伪指令的作用类似于普通变量的赋值操作,例如int a = 3;
,后续可以再次为a
赋值。
上述代码中,
MAXLEN
的值可以从 100 变为 200。关键区别
- 不可变性:使用
EQU
定义的符号在程序中只能被定义一次,其值在程序中不可改变。这类似于 C++ 中的const
常量,一旦定义就不能修改。这样可以确保程序的某些关键值在运行时保持不变,提高程序的稳定性和可预测性。
- 可变性:使用
=
定义的符号可以在程序中多次被重新定义,每次定义都会覆盖上一次的值。这类似于普通变量的赋值,可以在不同的地方进行更新和修改。这种灵活性在某些场景下可能更为实用,但同时也带来了更多的风险,因为程序中的同一符号可以在不同的地方代表不同的值,可能导致逻辑错误。
ORG 伪指令

分支程序设计
单分支语句:只有一个条件判断语句。多分支语句:有多个条件判断语句。
程序调试时要保证每个分支的独立性,即要把条件成立的程序测试一遍,同时还要把条件不成立的情况测试一遍。
分支程序设计要点:
- 正确选择分支的条件和条件转移指令。
- 在编写程序时要保证每个分支程序的正确性。
循环程序设计
循环程序的组成:
- 初始化部分;
- 循环工作部分;
- 参数修正部分;
- 循环出口判定;
- 结果处理。
冒泡排序法:


总线及其形成(第五章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 总线
- 74LS244 74LS245 74LS373
- 最小工作方式 最大工作方式
- 各种引脚
- 8088 与 8086 的区别
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
总线的定义及分类
定义:
总线是一种用于在计算机系统的不同组件之间传输数据的通信系统。它可以将处理器、内存、输入/输出设备等连接在一起,使得这些组件能够互相交换数据。总线的作用类似于一条高速公路,它在系统中充当数据的“通道”。
分类:
- 片内总线;
- 元件级总线(板内);
对于频率比较高的总线,走线时尽量不要呈 90°,同时外部还要加上屏蔽网。
- 内总线(系统总线);
板与板之间连接的总线,像常见的计算机插槽间的总线就是内总线。本章主要涉及这一类总线。
- 外总线(通信总线)。
几种常用芯片的介绍
- 74LS244 器件(8 位数据单向缓冲器)
LS 指的是速度,74 指的是工作温度,74 系列的缓冲器主要适用于民间使用,而 54 系列的缓冲器更多的是军用,价格更高,工作温度范围更广。

当 时,输出 1Y1 ~ 1Y4 随着输入 1A1 ~ 1A4 变化,当 时,无论输入怎么变,输出都不会发生变化。
控制的是 2Y1 ~ 2Y4 是否随着输入 2A1 ~ 2A4 输出,以此类推。
2. 74LS245(八路双向总线收发器)



- 74LS373(8位锁存器)
- 74LS374
- 74LS138





如 CBA = 000 时, 。
8086 cpu 引脚功能
MN/MX非 (最小工作方式与最大工作方式)


最小工作方式(最小方式):给外部存储器 or I/O 的读/写控制信号由 CPU 引脚直接产生(单处理系统)。
最大工作方式(最大方式):不是由上述器件产生,而是由 8086 CPU 外部的总线控制芯片 8088 产生(多处理系统)。

最小工作方式:

最大工作方式:

最小方式下的引脚功能:
CLK 信号

RESET 引脚
cpu 通过 RESET 端口进行复位,RESET 为复位引脚,高电平有效,高电平有效时间至少大于等于 4 个时钟周期 T。

DEN 非引脚和 DT/R非 引脚
:

:

M/IO非

WR 引脚和 RD 引脚
:

:

~ 引脚

在 状态下,可以使用 74LS373 器件将地址总线上的地址锁存,锁存的地址就称为系统级的地址总线。在其他 T 状态下,总线就可以有其他的用途。



ALE 是用于控制 74LS73 的 G 端口的,用于控制 74LS73 是否锁存数据,上升沿跟随,下降沿锁存。
ALE 引脚

READY 引脚

BHE非/S₇
BHE 又称为高字节允许信号。

上面的表 5.2 第一次看可能不太理解,在后面进行讲解后会有更深的认识。
在 8086 内部, 用于奇地址存储器的片选,而 地址线用于偶地址存储器的片选,它们都是低电平有效的。对于 的低电平有效其实比较好理解,当地址为 0000 0010 的时候,表示的是偶地址,因为最后一位是 0,而这也意味着 。至于为什么要将 和 分开,目前我还是不太理解。
如下图所示,奇地址存储器中的内容只能通过高 8 位数据线传输,偶地址存储器中的内容智能通过低 8 位数据线传输。

为了对表 5.2 有更好的理解,我们可以举以下几个例子:
看完上面的四个示例,现在再看表 5.2,应该会更加清晰了。

NMI INTR INTA非

HOLD 和 HLDA

8086 引脚示例

常用引脚

最大方式的特点

最大方式就是通过 ,, 的组合让 8288 发出控制信息,与最小方式不同,并不是通过 cpu 本身发出信息来进行控制的。
最小方式最大方式总结




当题目描述为在 8088 系统中,而没有提及究竟是最大系统还是最小系统时,一律将其看作是最大系统。
8086 与 8088 的区别

- 数据总线宽度:
- 8086:拥有 16 位的数据总线,这意味着它可以一次性传输 16 位的数据。因为数据总线宽,所以它在处理器和内存之间的数据传输速度更快。
- 8088:只有 8 位的数据总线,因此它每次只能传输 8 位数据。虽然它的内部结构仍然是 16 位的,但因为外部总线是 8 位的,所以数据传输速度会比 8086 慢一些。
- 性能影响:
- 由于 8086 的数据总线是 16 位的,它能够更快地从内存中读取和写入数据,这使得它在处理大量数据时性能更好。
- 8088 的 8 位数据总线虽然在性能上不如 8086,但它与当时的许多 8 位系统硬件兼容性更好,成本也更低。
- 的影响:
- 在8086 系统中,地址分为奇地址和偶地址,需要通过 来进行寻址,而在 8088 系统中,没有 ,也没有奇地址和偶地址的分别。
这一章节的内容我当时看的没有特别的认真,后面一定好好再补补🐶(对不起‼️周老师😶🌫️)
存储器设计(第六章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- ROM SRAM
- 存取时间 存储周期
- 6264 存储器
- 位扩展 字节扩展
- 8088 系统存储器扩展设计
- 8086 系统存储器扩展设计
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
存储器分类
外存——暂存暂时不用的数据和程序叫做外存,如硬盘。
内存——存储现在要用的存储器和数据叫做内存,大多由半导体构成,又称半导体存储器。

在本门课中,主要是设计 静态 SRAM。SRAM 常用的是 62 系列,如 6264(,本门课程需要掌握的) 6212()。

设计的技术指标
- 容量上,尽量选择容量大的器件来进行设计。
使用太多容量小的器件会增大系统的功耗,增加散热。
- 速度。

最小读出时间和最小写入时间:

最后再减去 T 是为了留出一定的富余量,在工程上一般要让器件留有 30% 的富余量。
的理解:信号的反射会对信号造成延时,当两个器件之间的阻抗不匹配的时候,一个器件发出的信号另一个器件吸收不完全,就会发生反射,反射回来的信号又会因为吸收不完全而再次反射回去,造成延时。

还有一种情况也会造成 ,即当两根传输线挨得比较近的时候,会有耦合电容的存在,耦合电容的大小与线的长度以及线与线之间的距离有关。耦合电容的存在会使得交流信号对其他线路产生影响。
存储器芯片的介绍
6264 存储器
存储器内存是 8k X 8. 即地址总线是 13 根,每个存储单元的容量是 1 个字节,8位。

6264 存储器是双片选存储器,只有当 的时候,存储器才有可能是工作的,一般情况下 都接高电平,于是器件就会称为是单片选器件。 和 两个引脚分别用于读操作和写操作,读操作是通过地址总线读取 的数据,写操作是相反的,将 的数据写入地址线 指向的地址单元。
6264 存储器在 8088 系统与 8086 系统中的连接:

位扩展和字节扩展

存储器扩展设计
8088 系统:


第三问涉及的检修程序有以下几种:
- (粘连故障)55H 和 AAH 法。向器件输入1 个字节的数据,看输出是否正常。55H → 01010101 B;AAH → 10101010 B。
- (链桥故障)谷值/峰值检测。
第二小题:

8086 系统:


常用芯片的接口技术(第七章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- I/O 接口电路 接口技术
- I/O 端口地址 信息的种类
- 独立编址 统一编址
- 无条件输入/输出
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
什么是常用芯片?
输出端口: 74LS373 74LS374 74LS273
输入端口:74LS244 74LS245
I/O 接口概念
由于设备种类繁多,要求通信的信息格式多样,所以 CPU(计算机)与设备之间不能直接通信。
因此需要在二者之间设计一个电路将二者连接起来。该电路就称为 I/O 接口电路。
对于 I/O 接口电路分为 I/O 硬件设计和 I/O 软件设计两种,它们合起来称为 I/O 接口技术。
I/O 接口组成
一种信息通过一个 I/O 端口进行传输,一个 I/O 端口要分配一个 I/O 端口地址。
信息的种类有三种:① 数据信息(入/出);② 状态信息(入);③ 命令信息(出)。

I/O 端口是用寄存器的设计来实现的。
I/O 地址的编址方式
- 独立编址(用于 8086 系统)。

I/O 地址寻址只有两种方式,一种是直接寻址,另一种是对寄存器的间接寻址。独立编址方式比较不灵活。寻址方式越多,就越灵活。
- 统一(映像)编址。
外部存储器与外部的 I/O 统一共享了地址。cpu 将外部 I/O 系统同样当作是一个存储器单元来使用,因此对于外部存储器的指令,对于外部的 I/O 端口依然适用。但缺点是外部 I/O 端口占用了外部存储器的地址,浪费了存储器的存储空间。

输入输出方式
程序直接控制的输入输出方法(in/out 指令)
- 无条件输入/输出方式。
只要有输入数据,那么输出设备无条件就会接收,如外接一个 LED 灯。
或者是通过开关控制是否输入数据的也是无条件的,永远处于数据准备好的状态。

cpu 与输出设备之间需要设计一个锁存器来锁存数据,因为 cpu 不可能一直与一个输出设备连接,让该输出设备一直占用总线资源,通过锁存器可以让输出设备保持在锁存值的状态下。锁存的端口大多使用 74LS374 或是 74LS273,但是很少使用 74LS373,因为 373 的 G 端口是高电平有效的,会造成系统的不稳定。
- 查询法输入输出。

电路实现简单,但是效率低。举一个夸张的例子,当 cpu 在 8 点开始运行的时候,查询设备状态,如果设备没有准备好,那么 cpu 就会继续查询,很可能到了 12 点都还在查询过程中,始终没有准备好。
查询法例题:
题目:
A/D 变换器的电原理图与主要工作时序如下:

A/D 变换器能否对 端口进行采集变换受控制信号 控制,当给 输入正脉冲的时候,正脉冲的下降沿启动 A/D 变换,A/D 变换需要一定的时间,在这段时间内,会输出状态 EOC。在变换时间结束后,有效数据才会经过 进行传输。
题目要求:数据的输出需要有一个数据输出端口,数据输出端口的地址为 8001 H;还需要设计一个状态输出端口用于输出 EOC 的状态,地址为 8003 H;要启动变换,需要设计一个命令输出端口输出一个命令给 ,地址为 8005 H。
(1) 将此 A/D 变换器与 8086 最小变换总线系统连接起来;
(2)编写对 采集一次,并将变换后的数据存入 ADBUF 单元的程序。
解答:

因为取的 8001 H, 8003 H, 8005 H 都是奇地址,因此最低位可以使用 来代替。
- 中断法。

使用中断法来进行输入输出大大提高了效率,但是 8259 内部的设计却十分复杂,不过好在 8259 在设计好后可以作为芯片量产。
- DMA 方法(硬件的方法)→ 硬件复杂,但是输入输出全靠硬件实现,在本门课中不要求掌握。
中断系统与可编程中断控制器(第八章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 中断 中断源
- NMI INTR
- 执行中断的过程
- 中断向量表 中断向量
- INT 21H
- BIOS 功能
- 8259A 引脚及功能 级联
- 8259A 内部组成
- IBM PC/AT 机 端口地址 中断类型号基值
- 初始化命令字编程
- 操作命令字编程
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
中断的基本概念
中断的定义:
计算机在程序运行过程中,由于发生了某些“紧急事件”,需要进行特殊处理(转向中断服务子程序执行),处理后返回到程序中断处继续执行,这种过程称为中断处理过程。
中断源的定义:
谁向CPU引起了中断请求,就是那个中断源,中断源可以在CPU内部,也可以在CPU外部。
中断服务子程序指的是为提出中断请求的中断源服务的程序就称为中断服务子程序。
8086外部中断源请求输入引脚有两个,一个是NMI,一个是INTR。
对于NMI外部中断输入响应请求,CPU要无条件响应,这种请求称为外部非可屏蔽中断请求。
对于 INTR 外部中断请求,CPU 是否响应受 IF 中断标志位的控制。
中断的分类:
内部中断、外部不可屏蔽中断、外部可屏蔽中断。
8086 的中断结构

对于上图中的中断结构,可以分为三大类来看——硬件中断、软件中断(简称“软中断”)和特殊中断。
硬件中断包括外部可屏蔽中断(INTR)和不可屏蔽中断(NMI)。
软件中断是由指令 INT n 产生的,包括 ROM-BIOS 中断(n 取 10H~1FH, 05H,40H, 41H, 46H)和 DOS 系统中断(取 20H~3FH)。需要特别注意的是,不可屏蔽中断 NMI 占用中断号 n = 2,即 INT 2 为 NMI。
特殊中断是内部突发事件引起的中断,也是不可屏蔽的中断,其处理过程类似于软中断,有时也归为软中断。因此特殊中断也会占用中断号 n。
当 n 为 0 的时候,触发除数为 0 中断(当除数为 0 时自动触发此中断);
当 n 为 1 的时候,触发单步中断( TF 为 1 时 触发此中断);
当 n 为 3 的时候,触发断点中断,一般用于 DEBUG;
当 n 为 4 的时候,触发溢出中断,当 OF 为 1 时具备触发此中断的条件,但请注意,此中断下,CPU并非自动转入溢出处理程序,OF=1 只是溢出中断的一个必要条件。用户在编程时,若要对某些运算操作进行溢出监控,就应在这些操作指令后面加上INTO指令,并设计相应的溢出中断服务程序。当OF=0时,INTO不会产生任何操作。
中断优先级:
软中断(包括 INT n 中断、除法中断、断点中断、溢出中断等) > NMI > INTR > 单步中断;
NMI 中断请求:
上升沿有效,CPU 无法拒绝的中断。
INTR 中断请求:
高电平有效,CPU 可以拒绝。般情况下用 STI 指令使 IF=1,确保中断开放。
该引脚会外接一个中断控制器(如 8259 A),中断控制器可以接受多个外部中断的请求,并通过 INT 端口与 cpu 联系。中断控制器还会有 ~ 8 个端口与 cpu 相连,用于 cpu 初始化中断控制器,可以在初始化的过程中主动屏蔽掉一些端口的中断请求,这样,不管 IF 是否置 0,中断控制器本身就会拒绝某些中断的输入。
在初始化的过程中,用户也需要分配中断控制器与外部中断源的中断类型号,但只需要手动分配 的即可,剩余的 IR 端口系统会顺序分配。
系统处理中断的过程
当遇到中断请求后,第一步,cpu 会将正在执行的指令执行完;
第二步,如果是外部可屏蔽中断,在 IF = 1 的情况下,cpu 会保护中断现场,按照 FR, CS, IP 的顺序入栈保护;
第三步,如果是外部可屏蔽中断,那么就需要从 8259 中获取中断信号,执行中断程序;如果是内部中断或是 NMI 中断,由于类型号是固定的,那么就不会有第三步的过程,直接进行第四步;
第四步,清除 IF TF 标志位,清 IF 的目的,是为了确保在运行中断服务程序的过程中不受外部中断的干扰。清 TF 的目的,是让 cpu 在执行中断服务的指令时能够连续执行下去,若 TF =1,则会触发下一步的中断,所以清 TF 是为了防止出现陷阱中断。
第五步,将受到中断请求时的 n 乘以 4 得到中断向量的值(起始地址),将中断向量表中对应的起始地址赋值给 IP,起始地址的下一个字地址赋值给 CS;
第六步,执行地址所对应的中断服务程序。
等到中断程序执行完后,会执行 IREP 指令,PSW, IP, CS 又会返回,称为现场恢复。

从 cpu 收到中断请求到执行中断服务程序一共经历了 5 步,这 5 步称为是外部可屏蔽中断的响应过程。
中断向量表
中断向量指的是中断服务子程序的入口地址。
一个中断向量在字节表中存放占了四个字节,因为一个中断向量是 32 位,包括 16 位段地址和 16 位偏移地址。中断向量表的大小是 1024B,可以存放 256 个中断向量。

DOS 中断 INT 21 H 的功能
入口参数都是 AH。
从键盘输入字符(01 H, 07H, 08H):

调用方法:
向屏幕输出一个字符(02H):


调用方法:

实现回车+换行(02 H):

控制台输入/输出(06H):

DL 中存放 0FFH 的时候,表示的是控制台输入;DL 中如果想要输出字符,存放的就是输出字符的 ASCII 码。
向屏幕输出一串字符(09H):

显示系统时间(2DH):


终止进程(4CH):

BIOS 功能(部分)
通过 INT 10H 进行中断调用,入口参数为 AH。
显示器清屏功能(00H):

设置光标位置(02H):

可编程控制器 8259A 及其应用
引脚及功能(外部特性)

和 既可以进行写操作也可以进行读操作, 是片选信号,即是否允许 8259 A 启动, 是片内寻址的地址线,在 的情况下, 如果为 0,那么就会选中一个 I/O 端口地址,如果为 1 则会选中另一个。虽然只能选中两个端口地址,但是 8259 A 的内部却远不止一个寄存器存在。 

INT 引脚是用于向 cpu 发出中断请求的,与 cpu 的 INTR(外部可屏蔽中断)连接。当 cpu 响应请求时,会从 cpu 的 引脚向 8259 A 的 引脚发送两个负脉冲信号,第一个负脉冲信号用于告诉 8259A 它发出的中断请求 cpu 响应了,第二个脉冲信号是告诉 8259 A 将对应外设发出的中断类型号发送给 cpu,同时 cpu 内部也会在第二个负脉冲的总线周期中自动读取中断类型号。
8259A 的 引脚应该与 cpu 的 引脚相连,因为在 cpu 发送第二个负脉冲时,只有低 8 位做好了接受数据的准备。

老师说,级联缓冲基本上是不会使用的, 一般不处于缓冲方式。
在级联方式下,由主片与 cpu 进行沟通交流,8259A 收到中断信号时,会汇总给主片,由主片向 cpu 的 INTR 端口发送信号,cpu 响应后,会将负脉冲信号发送给主片。假设主片的 端口与某个从片级联,主片收到第一个负脉冲后,8259A 会从 发出从片选择代码 001,从片通过 接受 001 的代码并检测是否匹配,与 001 代码匹配的从片会处于准备状态,随时准备输出中断类型号,当 cpu 发送第二个负脉冲后,从片会将中断信号发送给 cpu。
内部组成

器件介绍:




中断结束方式:
① 自动结束方式(一般不使用)。它的作用是将中断类型号发送给 cpu,同时将 ISR 中的对应位清零。
② 非自动结束方式。它在将中断类型号发送给 cpu 的时候,并不会将 ISR 对应位清零,而是在 cpu 运行完中断结束程序后,发出指令告诉 8259 A 我的运行要结束了,让 8259A 先结束。
因此,外部可屏蔽中断不仅受 IF 控制,还受 8259 A 内部是否会屏蔽的影响。

初始化命令字 :
ICW1 可以设置 8259 A 是单片工作还是级联工作,还可以设置 8259 A 输入信号的有效形式是高电平还是低电平、上升沿等;
ICW2 用于设置中断类型号的基址(即 的地址),一旦基址确定了,其他地址也就确定了;
ICW3 只有在级联时才会发挥作用,用于告诉从片它的级联输出被级联在主片上的哪个输入了;
ICW4 用于设置缓冲方式、中断结束方式以及 优先权的排列方式,是固定优先权(优先级由 逐级递减,又称正常嵌套方式)还是循环优先权(指的是最开始的时候 最高, 中断请求响应后, 级就最高, 级就最低,以此类推)。
操作命令字 :
给 OCW1 写命令,相当于给 IMR 中写命令;
给 OCW2 写命令只在 8259A 处于非自动结束方式时有用,用于 cpu 快结束中断程序时让 8259A 先结束命令;
OCW3 一般不会使用。
IBM PC/AT 机
8259A 的工作过程

8259A 的编程
初始化命令字编程




操作命令字编程


例题:
题目:

题目中 “中断请求” 部分可以看作是一个锁存器, 是一个清零端。0BH 指的是 IR₃ 的中断类型号基值。
求解过程:
这个例题还是很复杂的,但老师说考试不会这样考,但是上面的例题有助于理解编写中断程序的步骤。
可编程定时器/计数器芯片 8253/8254 及其应用(第九章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 引脚功能
- 8253 内部组成
- 定时器
- 单脉冲形成
- 分频器
- 方波发生器
- 软件触发/硬件触发
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
可编程定时器/计数器 通常用于在器件固定的情况下,我们希望对器件的功能作出调整的时候。
引脚功能


从引脚定义中就可以注意到,8253 内部是没有 RESET 这个端口的,因此在工作期间系统 RESET 了对 8253 无影响。
8253 内部共有 3 个 16 位的计数器,其中每个计数器都有 GATE, CLK 和 OUT 这 3 个端口以及 6 种工作方式(方式 0 ~ 方式 5),通过下标来进行区分。有些工作方式是高电平有效,在门控信号为高电平的时候,可以对 CLK 进行计数。也有些工作方式是边沿有效或是低电平有效。
CLK 用于输入计数器的计数时钟,下降沿有效。
OUT 会定时时间到输出端(分频)。

和 端口用于选择端口,片内寻址。
8253 对于每个计数器都有一个控制字寄存器,它是通过控制字来控制具体选择哪一个计数器的。
8253 的内部组成及工作原理

cpu 设置计数器初值时是先写低 8 位 ,后写高 8 位 ,因为数据线只有 8 位;
在写完初值后,在第一个时钟下降沿就会把 和 (分别为计数器寄存器 CR 的低 8 位和高 8 位)的值写入减法计数器 。
在 8253 的内部,还有与计数器匹配的锁存器,如果是与第一个计数器匹配,那么它就是 (输出锁存器的低 8 位)和 (输出锁存器的高 8 位),锁存器的值会始终随着对应减法计数器 的值变化而变化。
用户可以通过 cpu 读取到 的值,但由于cpu 的时钟和计数器的时钟不同,因此想要读取 的值时,cpu 要先发送命令将锁存器的值锁住,然后发送命令读取锁存器的值,当锁存器中的值被读取后,锁存器就会自动开锁,此时它的值又会跟随 的变化而变化。
输入的 CLK 不能超过 2MHz
方式控制字寄存器

工作方式介绍
每个计数器都有方式 0 ~ 方式 5 六种工作方式。
方式 0(定时器)
方式 0 介绍(课本内容)


特点(课本内容)


当 在计数过程中经历了一个下降沿后,计数器会先停止计数,等待 变高后再继续(GATE 为电平控制时):

方式 1(单脉冲形成)
方式 1 介绍(课本内容)

特点(课本内容)


⭐!重点记!⭐ 方式 2(分频器)
方式 2 介绍(课本内容)

特点(课本内容)


与方式 0 不同,方式 2 的分频信号是自动写入的,也就是说,在一开始设置了分频系数后,后面计数器只要计数到 0 就会自动打入 6 重新开始计数
对于方式 2,分频的效果最终是体现在 OUT 端口的,因为 OUT 端口要每隔 N 才会重新开始计数。
⭐!重点记!⭐ 方式 3(方波发生器)
方式 3 介绍(课本内容)


特点(课本内容)



方式 4(软件触发产生选通信号)
方式 4 介绍(课本内容)

特点(课本内容)

方式 5(硬件触发产生选通信号)
方式 5 介绍(课本内容)

特点(课本内容)

6 种方式的总结

计数器的应用
计数器除了能够作为定时器唤醒 cpu 外,还有一个比较有趣的应用——看门狗。
有时候,在运行某段程序时,cpu 出了点问题, IP 指针突然指向了其他地方,不再在程序段内,那么很可能会导致 cpu 死机,即俗称的“程序走飞了”。
为了应对这个问题,我们可以在程序内部定点设置计数器,即看门狗,计数器的计数值恰好是运行程序段所需要的时间,或是略大于这个程序段的运行时间(因为程序的每条指令都是有总线周期的,因此可以实现),当计数器的数值达到预设的数值时,程序停止运行,这样就可以避免 IP 指针跳转到其他地方无法退出指令,造成死机。
当然,在某些情况下,我们希望程序能够跳转到原先走飞的位置,而并非在走飞后从头开始运行程序。为了实现这个目的,我们可以在程序段内每隔几条指令就设置一个计数器,计数器的计数值就是运行到下一个计数器所需要的时间。这样每运行一段程序,计数器的值都需要增加才能保证程序正常运行(应该是叫做“喂狗”),当程序走飞后,看门狗会记录走飞的位置,及时把 ip 叫回来,提高了系统的稳定性。
可编程并行接口芯片8255A(第十章)
💡 Key Words
这里的关键词只是帮助大家看完右侧的笔记后回忆内容,不是跳转链接!!
- 8255A 引脚及功能
- 方式 0 方式 1 方式 2
- 工作方式控制字
- 读取端口 C 状态
🔗 Relevant Information
第一版:
第三版:
📝 Class Notes
现实应用中还有可编程的串行器件 8250 和 8251(姊妹片)
为什么需要 8255A?

8255A 的引脚及功能
共有 40 个引脚,分为与外设相连的引脚和与 cpu 相连的引脚来记。


与外设相连的部分:
由于是并行接口,一个端口是 8 位,共有 3 个端口,因此输出端口共 24 个引脚。三个端口分别叫做 PA, PB, PC 。
PA 口和 PB 口都是可编程的,既可以将外设的数据传输到 cpu,也可以将 cpu 的数据传输到外设。
PC 口可以当作 8 位用,也可以当作是两个 4 位用,在实际应用中, 可以辅助 PA 口作用,将它们合起来称作是 A 组。 可以辅助 PB 口作用,将它们称作是 B 组。
在 8255A 内部有一个工作方式控制器端口,是 8 位的。
与 cpu 相连的部分:
RESET 是复位端口,交给芯片的复位信号宽度应该小于等于 cpu 的复位宽度。
是片外寻址端口, 和 是片内寻址端口,它们三者结合起来可以选择对应的端口。
8255A 内部有四个端口(PA, PB, PC, 控制端口),需要通过 A₀ A₁ 来进行片内寻址。

8255A 的工作方式与工作方式控制字
工作方式:
方式 0 :无联络信号的输入/输出,相当于是无条件输出的方式,使用 PA/PB/PC 作为输入/输出端口
方式 1 :有联络信号的输入/输出,使用 PA/PB 作为输入/输出端口,PC 端口不能用于输入/输出,因为它的高四位要作为联络端口与外设之间进行联络;
方式 2:双向端口(准双向),只能使用 PA 口。
在方式 0 和方式 1 中,一个端口传输数据时是作输出还是输入,它就只能是作为输出还是输入。而方式 2 下的端口既可以作输出,也可以作输入,但是不能同时为输出输入,因此为准双向的。
为什么方式 2 只能使用 PA 口?
因为在 PA口用作输入的时候,它需要两根 PC 线来进行联络通讯,其中一根用于外设发送数据告诉 8255 “我发送数据过去了”,另一根用于8255 发送数据给外设告诉外设“我把数据取走了”。
同理,在 PA口用于输出的时候,它也需要两根 PC 线来进行联络通讯,输入输出一共占用了 4 根 PC 线。上面使用的 4 根 PC 线都是 A 组的,都是高位的 PC 线。
但是 8255 还需要用一根 PC 线作为中断,在前面 4 根 PC 线已经被占用的前提下,只能向 B 组借一根 PC 线用于中断,那么 B 组就没有足够的 PC 线构成 PB 的准双向输入输出,因此只能使用 PA 口。
工作方式控制字:

例题:
题目:

求解过程:

方式 1 输入信号的具体过程:



首先,假设外设是通过 PB 口向 8255 A 发送数据的,由于 上一直有数据存在,因此 外设 需要有一个端口连接到 8255A,当有有效数据传输过来时,告诉 8255A “我要发送有效数据了”。这个端口就是选通端口 (strobe, 端口),它的下标 B 表示是 B 端口。
外设要发送数据时,会提前从选通端口发送一个低电平给 8255A,8255A 收到信号后会将外设发过来的 8 位数据暂时先存放到缓冲器中,在 cpu 取走数据前,8255A 都不能接收新的数据进来了。因此,在 8255A 上还有一个 (input buffer full)端()口用于判断缓冲器是否已满,如果缓冲器满了,它会发出高电平,告诉外设缓冲器已经满了,不能再发送新的数据过来了。
cpu 查询数据是否能够取走的方法有以下几种:
查询法:
对于 cpu 而言,如何知道 8255A 缓冲区数据已经满了呢?它会通过一次 in 操作查询 8255A 的 端口是否为高电平,如果是高电平,那么 cpu 就会将数据及时取走。cpu 取走数据后, 端口信号会再次变低,此时外设就又可以写入数据了。
与方式 0 的区别:方式 0 中,cpu 在任何情况下都可以读取到有效数据,而在方式 1 下,只有缓冲器是满的时候才可以读到数据。
中断法:
在 8255A 的内部有 和 两个中断触发器,只有在中断触发器置 1 的时候才可以触发中断(由编程决定是否置 1),在 8255A 中有一个类似与门的装置,可以将 INTE 和 IBF 的信号相与,只有当 INTE = 1(允许中断)以及 IBF=1 (缓冲器满了)才可以输出 1,与门的输出会连接到 端口(对于 PB 端口而言)。 会将数据发送给 8259A,如果此时 IF = 1,8259A 就会向 cpu 发送中断请求,cpu 响应中断请求后会执行中断程序,将缓冲器的数据读取。

PA 端口的过程与上面一致,只不过 和 的端口分别是 和 ,如果使用中断信号则使用 。
剩下的两根 PC 线 和 仍然可以用作是 I/O 线,作为方式 0 输入或输出。
A 组和 B 组使用中断屏蔽器的时候使用的 PC 端口:

当 A 组作输入方式的时候,控制位为 ,当 A 组作输出方式的时候,控制位为 。
B 组无论工作在哪种方式下,都是用 作为输出端口。
方式 1 输出信号的具体过程:



在 PA 口工作在输出方式下时,由 端口传输数据给输出设备,当 cpu 将数据传输给 8255A 时,由 8255A 发出 (output buffer full)信号告诉输出设备“可以过来取数据了”,由设备的 端口进行接收。设备接收到 8255A 发来的信号后,会从 8255A 处将数据取走,并向 8255A 发送 (acknowledge input)信号,告诉 8255A “我已经取走信号了”,这时 cpu 便可以发送新的信号过来了。
由 和 充当端口 A 的联络线。
在作输出方式的时候,8255A 通过中断法与 cpu 进行联络,首先, 中断触发器会置 1,然后和信号 相与,当 为低电平时(因为 只有在低电平的时候表示满),才会向 cpu 发送高电平的 信号。
剩下的两根 和 的线也可以用作 I/O 口的输入或输出。
方式 2 输入输出信号的具体过程:

读取端口 C 状态

8255A 与系统总线的连接

8255A 的应用(例题)
例题 (例 10.1):
题目:

求解过程:

例题(例 10.3):
题目:

注意:题目中的端口地址 20H, 22H, 24H, 26H 分别对应的是 8255A 的 PA口,PB口, PC 口 和控制端口。
原因如下图所示:

8255A 将使用 cpu 的 和 端口作为选择端口。
求解过程:

老师的代码示例:
如果本篇笔记对你有用,能否『请我吃根棒棒糖🍭 』🤠…