汇编基础
基础知识
1.1 机器语言
- 机器语言是机器指令的集合。
- 机器指令是一系列二进制数字,计算机将之转换为一系列高低电平脉冲信号来驱动硬件工作的。
1.2 汇编语言的产生
- 由于机器语言指令都是由01组成,难以编写,记忆和维护程序.所以汇编语言为了解决这一问题产生。
- 汇编语言的主体是汇编指令,汇编指令是机器指令的助记符。
- 寄存器: CPU中存储数据的器件,一个CPU中有多个寄存器。
1.3 汇编语言的组成
- 1、汇编指令(机器码的助记符,有对应的机器码);
- 2、伪指令(由编译器执行)和其他符号(由编译器识别)。
1.4 存储器
- CPU工作需要指令和数据,指令和数据存储在存储器中。
1.5 指令和数据
- 在内存或者磁盘中存储的都是为二进制信息,指令和数据由我们设定(走的总线)。
1.6 存储单元
- 存储器被划分为若干个存储单元,每个存储单元从0开始顺序编号。
- B、KB、MB、GB、TB等单位。
1.7 CPU对存储器的读写
CPU要对数据进行读写,必须和外部器件进行以下三类信息的交互:
- 1、存储单元的地址(地址信息);
- 2、器件的选择、读或写命令(控制信息);
- 3、读或写的数据(数据信息) 。
总线是连接CPU和其他芯片的导线,逻辑上分为地址总线、数据总线、控制总线。
逻辑上总线的分类
CPU从内存单元中读写数据的过程:
- 1、CPU通过地址线将地址信息发出;
- 2、CPU通过控制线发出内存读命令,选中存储器芯片,并通知它将要从中读或写数据;
- 3、存储器将相应的地址单元中的数据通过数据线送入CPU或CPU通过数据线将数据送入相应的内存单元。
1.8 地址总线
- CPU是通过地址总线指定存储单元,地址总线传送的能力决定了CPU对存储单元的寻址能力。(一般32位CPU,寻址能力为2^32=4G)
1.9 数据总线
- CPU通过数据总线来与内存等器件进行数据传送,数据总线的宽度决定了CPU和外界的数据传送速度。
1.10 控制总线
- 控制总线是一些不同控制的集合,CPU通过控制总线对外部器件的控制。控制总线的宽度决定了CPU对外部器件的控制能力。
小结
- 1、汇编指令时机器指令的助记符,与机器指令一一对应。
- 2、每一种CPU都有自己的汇编指令集。
- 3、CPU可以直接使用的信息在存储器中存放。
- 4、在存储器中指令和数据都是二进制信息。
- 5、存储单元从0开始顺序编号。
- 6、一个存储单元可以存储8个bit。
- 7、B、KB、MB、GB等单位之间的转换。
- 8、CPU管脚和总线相连。总线的宽度表示CPU不同方面的性能:
- 地址总线的宽度决定了CPU的寻址能力;
- 数据总线的宽度决定了CPU与其他器件进行一次数据传送的量;
- 控制总线宽度决定了CPU对系统中其他器件的控制。
检测点 1.1
检测点1.1
1.11 内存地址空间(概述)
- CPU可寻的内存单元构成这个CPU的内存地址空间。例如一个CPU的地址总线宽度为10,那么可以寻址的1024个内存单元构成了这个CPU的内存空间。
1.12 主板
- 主板主板,主要的电路板 :laughing:
1.13 接口卡
- CPU通过接口卡间接控制外部设备。
1.14 各类存储器
随机存储器RAM(主板上的RAM、拓展插槽上的RAM和接口卡上的RAM)和只读存储器器ROM(装有BIOS的ROM)。
PC集中各类存储器的逻辑连接
1.15 内存地址空间
各类存储器在物理上是独立的,但是:
- 1、都和CPU的总线相连;
- 2、 CPU对他们进行读或写的时候都通过控制线发出的内存读写命令。
将各类存储器看作一个逻辑存储器
不同的计算机系统的内存地址空间分配情况是不同的。
寄存器(CPU的工作原理)
- CPU由运算器、控制器、寄存器 等器件组成,靠内部总线相连。
- 内部总线实现CPU内部各器件之间的联系;外部总线实现CPU和主板上其他器件的联系。
- 在CPU中:
- 运算器进行信息处理;
- 寄存器进行信息存储;
- 控制器控制各种器件进行工作;
- 内部总线连接各种器件在它们之间进行数据的传送。
通用寄存器
- 8086有14个寄存器:
- AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、CS、ES、PSW。
- AX、BX、CX、DX通常用来存放一般性数据,被称为通用寄存器。
- 16位寄存器所能存储的数据最大值为216-1 。
- 为保证兼容性,8086 CPU的通用寄存器可以分为两个独立的8位寄存器使用。例: AX可分为AH和AL。
字在寄存器中的存储
8086 CPU所有的寄存器是16位,可以存放2个字节(一个字)。
一字节由8 bit 组成,可以存在8位寄存器中。
字(word)是两字节,16位。
一个字由两个字节组成
几条汇编指令
汇编指令对大小写不敏感
汇编指令举例
汇编指令 | 控制CPU完成的操作 | 用高级语言的语法描述 |
---|---|---|
mov ax,18 | 将8送入AX | AX=18 |
mov ah,78 | 将78送入AH | AH=78 |
add ax,8 | 将寄存器AX中的数值加上8结果存入AX中 | AX=AX+8 |
mov ax,bx | 将寄存器BX中的数据送入寄存器AX | AX=BX |
add ax,bx | 将AX,BX中的内容相加结果存入AX中 | AX=AX+BX |
2.4 物理地址
- 所有的内存单元构成一个一维的线性存储空间。
- CPU访问内存单元时要给出内存单元的唯一地址就是物理地址。
2.5 16位结构的CPU
- 1、运算器一次最多可以处理16位数据。
- 2、 寄存器的最大宽度为16位。
- 3、寄存器和运算器之间的通路是16位。
2.6 8086 CPU给出物理地址的方法
- 8086有20位的地址总线,可以传送20位地址,寻址能力为1M;但8086内部为16位结构,只能传送16位的地址。
- 8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。
8086CPU相关部件的逻辑结构
- 8086CPU读写内存的步骤:
- 1、CPU中的相关部件提供段子和偏移地址这两个16位的地址;
- 2、段地址和偏移地址通过内部总线送入到一个称为地址加法器的部件;
- 3、地址加法器将两个16位地址合并成一个20位的地址;
- 4、地址加法器通过内部总线将20位物理地址送送入输入输出地址;
- 5、输入输出控制电路将20位物理地址送上地址总线;
- 6、20位物理地址被地址总线传送到存储器。
- 地址加法器工作原理:物理地址=段地址*16+偏移地址。
地址加法器的过程
- 段地址*16就是数据左移4位(二进制)
移位位数 | 二进制 | 十六进制 | 十进制 |
---|---|---|---|
0 | 10B | 2H | 2 |
1 | 100B | 4H | 4 |
2 | 1000B | 8H | 8 |
3 | 10000B | 10H | 16 |
4 | 100000B | 20H | 32 |
- 一个数据的二进制形式左移N位,相当于该数据乘以2的N次方。一个数据X进制形式左移N位,相当乘以NX。
2.7 段地址*16+偏移地址=物理地址
- CPU可以通过不同的段地址和偏移地址形成一个相同的物理地址。
CPU可以通过不同的段地址和偏移地址形成相同的物理地址
段地址*16是移位
2.8 段的概念
人为定义的,将若干地址连续的内存单元看作一个段。用段地址*16定位段的起始地址(基址),用偏移地址定位段中的内存单元。
段的概念
一个段的起始地址是16的倍数。偏移地址为16位,寻址能力为64K,所以段的最大长度也是64K。
检测点 2.2
检测点2.2
2.9 段寄存器
- 8086 CPU有4个段寄存器:CS(代码段)、DS(数据段)、SS(堆栈段)、ES(附加段),这4个段提供给8086CPU内存单元的段地址。
2.10 CS和IP
CS(代码段寄存器) 和IP(指令指针寄存器) 是8086CPU中最关键的寄存器,它们指示了CPU当前要读取指令的地址。在任意时刻CPU将CS:IP指向的内容当作指令执行。
8086CPU工作过程的简要概述:
1、从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器;
8086PC机刚开始启动时,CPU从内存FFFF0h单元中读取指令执行,FFFF0h单元中的指令时8086PC机开机后执行的第一条指令。
2、 IP=IP+所读取指令的长度,从而正确的指向下一条指令;
3、执行指令。转到步骤1,周而复始。
2.11 修改CS、IP的指令
- mov指令(传送指令) 可以改变8086CPU大部分寄存器的值,但不能用于设置CS、IP的值。
- jmp指令(转移指令) 可以用来同时修改CS和IP的值,格式为
|
|
2.12 代码段
- 对于8086PC机,在编程时可以将长度为N(N小于等于64KB)的一组代码存在一组地址连续、起始地址为16的倍数的内存单元中,这段内存是用来存放代码的,从而定义了一个代码段。
- 利用CS:IP来指向内存单元从而让CPU执行其中的内容。
检测点 2.3
检测点2.3
使用Debug
windows xp系统自带debug,请使用xp以上系统的读者执行自行下载debug.exe和dosbox,使用方法笔者不再赘述,在dosbox中可以使用debug。
debug in dosbox
- 可以使用汇编金手指查阅指令。
- R命令查看、改变CPU寄存器的内容;
- D命令查看内存中的内容;
- E命令改写内存中的内容;
- U命令将内存中的机器指令翻译成汇编指令;
- T命令执行一条机器指令;
- G命令跳转到偏移地址;
- P命令结束循环或者是int 21H时是退出程序;
- A命令是以汇编指令的格式在内存中写入一条机器指令。
三、寄存器(内存访问)
3.1 内存中字的存储
- 字是两个字节,要用两个地址连续的内存来存放,字的低位字节存在低地址中,高位字节存放在高地址单元中。
3.2 DS和[address]
- DS通常存放要访问的数据的段地址。
- 8086 CPU由于硬件的设计不支持将数据直接送入段寄存器的操作。
数据 -> 通用寄存器 -> 段寄存器
- 里边的数据代表偏移地址值
- mov指令:
- 将数据直接送入寄存器;
- 将一个寄存器或内存单元中的内容送入另一个寄存器;
- mov指令格式:
|
|
3.3 字型的传送
- 高地址单元和高8位寄存器,低地址单元和低8位寄存器相对应。
3.4 mov、add、sub指令
- 有两个操作对象,jmp只有一个操作对象。
- 使用汇编金手指查阅指令
- mov指令的几种形式
|
|
- add指令的几种形式
|
|
- sub指令的几种形式
|
|
3.5 数据段
- 对于8086PC机,在编程时可以将长度为N(N小于等于64KB)的一组代码存在一组地址连续、起始地址为16的倍数的内存单元中,这段内存是用来存放数据的,从而定义了一个数据段。
- 可以通过在DS中存放数据段的段地址,用相关的指令访问数据段中的具体单元来访问数据段中的数据。
检测点 3.1
检测点3.1
3.6 栈
- 具有特殊的访问方式的存储空间,也是内存空间的一部分,数据先进后出。
- 有两个基本操作:
- 入栈:将一个新的元素放到栈顶;
- 出栈:从栈顶取出一个元素。
- 栈顶元素最后入栈最先出栈。
3.7 8086 CPU提供的栈机制
- 现今的CPU都有栈的设计,基于8086CPU编程可以将一段内存当作栈来使用。
- 8086CPU的入栈(PUSH)和POP(出栈),以字为单位。
- push ax 将寄存器ax中的数据送入栈
- pop ax 从栈顶取出数据送入ax
- 段寄存器SS存放栈顶的段地址,寄存器SP存放栈顶的偏移地址。任意时刻SS:SP指向栈顶元素。push时SP先自减法后写内存,pop先读内存sp后自加。
- pop之后数据还是存在内存中,push时覆盖。
CS和IP存放当前指令的段地址和偏移地址。
3.8 栈顶越界的问题
- 栈是空的,则SP指向栈底+1的内存。
- 8086 CPU只纪录栈顶,栈空间由自己控制。栈顶越界问题导致溢出漏洞。
- 8086CPU只考虑当前的情况:
- 当前栈顶在何处;
- 当前要执行的指令时哪一条。
3.9 push、pop指令
- 可以直接对段寄存器使用。
|
|
- 通用寄存器命名是x结尾的,段寄存器是以s结尾。
- CPU在执行指令时,数据的段地址是从DS中获得,代码是在CS中获得,栈地址是从SS获得。
3.10 栈段
- 对于8086PC机,在编程时可以将长度为N(N小于等于64KB)的一组代码存在一组地址连续、起始地址为16的倍数的内存单元中,这段内存是当作栈来用,从而定义了一个栈段。
- 寄存器清零可用sub ax,ax或者直接赋值0,常见的也有使用xor。
- 当栈空间定义为最大时,栈为空时SP=0。
检测点 3.2
检测点3.2
四、第一个程序
引言
编写完成的汇编语言程序,用编译器编译成可执行文件并在操作系统中运行。
4.1 一个源程序从写出到执行的过程
编写
- 用编辑器(Sublime Text、Nodepad++、UltraEdit)编写,文件后缀为.asm。
编译链接
- 使用MASM.EXE编译生产obj(目标文件)。masm也请读者自行搜索下载。
- LINKE.EXE对目标文件进行连接生产可在操作系统中直接运行的可执行文件。
可执行文件包含程序(机器码)、数据(源程序中定义的数据)和相关的描述信息。
执行
- 操作系统中依照可执行文件中的描述信息将可执行文件中的机器码和数据加载入内存并进行相关的初始化,然后CPU执行。
4.2 源程序
- 汇编指令:有对应的机器码的指令,编译为机器码被CPU执行
- 伪指令:没有对应的机器码,不被CPU所执行,由编译器执行来进行相关的编译工作。
- segment和ends是用来定义一个段的,是成对使用的伪指令,再写可被编译器编译的汇编程序是必须要用的。
|
|
assume用来加上某一段寄存器和程序中的某一用segment……ends定义的段相关联。通过assume说明这种关联,在需要的情况下编译程序可以将段寄存器和某一个具体的段相联系。
一个汇编程序是由多个段组成。一个有意义的汇编程序中至少要用一个段来存放代码。
程序与源程序
程序与源程序
标号:指代地址
程序的结构
小练习:
|
|
- 程序的返回:一个程序结束后将CPU的控制权交还给使它得以运行的程序的过程。应该在程序的末尾添加返回的程序段。
codesg:放在segment前面,作为一个段的名称,这个段的名称最终将被编译、连接程序,称为一个段的段地址 。
|
|
- 语法错误和逻辑错误
4.3 编辑源程序
- 使用编辑器编辑,扩展名为.asm
|
|
4.4 编译
masn和 1.asm在同一目录中,dos下使用masm 1.asm命令即可生产1.obj文件。
源程序文件和masm文件在同一目录下
编译源程序
4.5 连接
link 1.obj,生成exe文件,摁enter忽略编译程序提示输入的信息。
连接程序
连接生成exe
当源程序很大时,可以将它分成多个源程序文件编译,每个源程序编译成目标文件后再用连接程序将他们连接到一起,生成一个可执行文件。或者程序中调用了某个库文件中的子程序,需要将这个库文件和该目标文件连接到一起,生成一个可执行文件。或者一个源程序编译后得到存有机器码的目标文件,目标文件中的有些内容还不能直接生成可执行文件,连接程序将此内容处理为最终的可执行文件信息。
4.6 简化编译和连接
- 使用ml命令,ml 1.asm
4.7 exe的执行
- 为兼容16位的程序,使用dosbox运行。
4.8 可执行文件中的程序转入内存并运行的原理
在dos中可执行文件中的程序p1若要运行吗必须有一个正在运行的程序p2将p1从可执行文件中加载如内存,将CPU的控制权交给它,p1才能得以运行;当p1运行完毕后,应该将CPU的控制权交还给使它de'yi 运行的程序p2。
汇编程序从写出到执行的过程:编程 -> 编译 -> 连接 -> 加载 -> 内存中的程序 -> 运行
在dos系统中.exe文件中的加载过程
exe文件中程序的加载过程
psp的内容
4.9 程序执行过程的跟踪
- 使用debug(xp以上的系统在dosbox中使用)来跟踪一个程序的运行过程。
使用debug来跟踪程序运行
五、[BX]和loop指令
引言
- 约定符号()来表示一个寄存器或者一个内存单元中的内容。例如(ax)=0010H表示ax中的内容为0010H;(21000H)=0010H,表示2000:1000处的内容为0010H。
- 约定符号idata表示常量。
5.1 [BX]
- inc指令是自增1的意思
- 和[0]有些类似,[0]表示内存单元,它的偏移地址是0。[bx]也是表示一个内存单元,它的内存偏移地址在bx中。
|
|
- 用以下两种信息描述一个内存单元:
- 1、内存单元的地址;
- 2、内训单元的长度(类型)。
我们用[0]表示一个内训单元时,0表示单元的偏移地址,段地址默认在DS中,单元的长度(类型)可以由具体指令中的其他的操作对象(比如说寄存器)指出。
|
|
5.2 loop指令
- 指令的格式是loop 标号。CUP执行loop指令时要进两步操作:
- CX中存放循环的次数,执行时CX中的内容自减1。相当于C的do while
- 判断CX中的值,不为0则转至标号处执行程序,为0则向下执行。
- 通常loop指令来实现循坏功能CX中存放循环的次数。
|
|
|
|
|
|
|
|
|
|
5.3 在Debug中跟踪用loop指令实现的循环程序
- 注意:在汇编源程序中数据不能以字母开头,有字母的在前面加0处理。
- t命令单步执行、G命令和P命令。
- 使用汇编金手指查阅指令。
5.4 Debug和汇编编译器Masm对指令的不同处理
Degug中mov ax,[0],表示将ds:0处的数据存入al中。ah=0,因为一个内存单元是8位的,ax是16位的,同位存储。而编译器[0]会被当作0处理
将内存2000:0、2000:1、2000:2、2000:3单元中的数据(字节)送入阿al、bl、cl、dl中。
- debug中:
debug 1
debug 3
- 在MASM中:
1masmtest
masmtest2
- 要在编译器中实现用偏移地址[]中的内容传送先bx来代替,mov 偏移地址,bx 再 mov al,[bx]。如要直接使用 则要加上段地址ds:[偏移地址]
在MASM中:
|
|
5.5 loop和[BX]的联合应用
- 可以用循环来解决处理地址连续的内存单元中的数据的问题,用变量来给出内存单元的地址。
5.6 段前缀
- 出现在访问内存单元的指令中用显式地指明内存单元的段地址的ds、cs、ss、es称为段前缀。没有显式地给出内存单元的段地址则默认在ds中。
5.7 一段安全的空间
在8086模式中,随意向一段内存空间写入数据是危险的,因为这段空间中可能存放着重要的系统数据或代码。
|
|
但笔者在练习的时候出现dosbox下debug卡死
dangeroustest
dos下0:200H0:2FFH的256个字节的空间是安全的,dos和其他合法程序一般都不会使用这段空间。内存0000:00000000:03FF大小为1kb的空间是系统存放中断处理程序入口地址的中断向量表。一般情况下0:200H~0:2FFH的256个字节的空间所对应的中断向量表都是空的,操作系统和其他应用程序都不占用。
dos安全空间
5.8 段前缀的使用
- 将内存ffff:0~ffff:b段单元中的数据拷贝到0:200 ~ 0:20b单元中
|
|
- 两个内存单元相差64KB则不再同一个段里,需要设置ds的值两次,效率不高。
- 使用 es(附加段)
|
|
六、包含多个段的程序
6.1在代码段中使用数据
- 编程计算0123H、0456H,0abxH、0defH、0fesH、0cbaH、0987H这8个数据的和,结果存放在ax中:
|
|
定义字型数据
- end的作用除了通知编译器结束之外还有告诉编译器程序的入口在什么地方。
- 可执行文件中的程序执行过程
6.2 在代码段中使用栈
- 利用栈编程将定义的数据逆序(联想栈的特性)存放:dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
|
|
- 如果对此程序的栈有疑惑,跳转到 3.6 栈和3.10 栈段
6.3 将数据、代码、栈放入不同的段
- 在8086CPU中数据、栈和代码存储空间不能大于64KB。可以用像定义代码段一样的方法来定义多个段并在其中定义需要的数据,或者通过定义数据来取得栈空间。
|
|
- 程序中指令决定了断中的内容是作为数据处理还是作为指令执行还是作为栈空间使用。
检测点 6.1
检测点6.1
实验五
|
|
七、更灵活的定位内存地址的方法
7.1 and和or指令
and指令:逻辑与指令,按位进行与运算。
and两个同时为真的结果才为真。
|
|
- 可用and指令将操作对象的相应位设为0,其他位不变
|
|
- or指令:逻辑或指令,按位进行或运算。
or两个同时为假的结果才为假
|
|
- 可用or指令将操作对象的相应位设为1,其他位不变
|
|
7.2 关于ASCII码
ASCII码表
- 将字符的ascii码写入显存屏幕就显示出相关的字符。
7.3 以字符形式给出数据
- 用‘’的方式指明数据是以字符的形式给出的。例如'A'
|
|
73 ascii
以字符形式给出数据
7.4 大小写转换的问题
- 大写字母比小写字母ASCII大32(20H)。
大写 | 二进制 | 小写 | 二进制 |
---|---|---|---|
A | 01000001 | a | 01100001 |
B | 01000010 | b | 01100010 |
C | 01000011 | c | 01100011 |
D | 01000100 | d | 01100100 |
- 从第0位开始计算,大写字母ASCII码第五位为0,小写字母ASCII码第五位为1。
|
|
7.5 [bx+idata]
- [bx+idata]表示的是一个内存单元,它的偏移地址为bx+idata
|
|
|
|
7.6 用[bx+idata]的方式进行数组的处理
- 用[bx+idata]的方式进行数组处理
|
|
- C语言的形式
|
|
7.7 SI和DI
- SI和DI在8086CPU中和bx功能相近,充当BX的扩充,但是不能分成两个8位寄存器来使用。[SI]段地址默认也是在DS中。
- 下面的指令实现了相同的功能
|
|
- 一般ds:si指向要复制的原始空间,ds:di指向复制的目的空间。
|
|
7.8 [bx+si]和[bx+di]
|
|
7.9 [bx+si+idata]和[bx+di+idata]
- 常数后要加.例如[bx+si].idata或者[bx].idata[si]
|
|
7.10 不同的寻址方式的灵活应用
- 编程将数据段中每一个单词的头一个字母改为大写字母。
数据段中的数据存储结构_2
|
|
- 编程将数据段中每个单词改为大写字母
数据段中的数据存储结构2
|
|
- 程序没有返回到cmd
712bug
712bug1
712bug2
|
|
- 因为loop是和cx一起使用的,不能多用个寄存器来解决loop循环次数的问题。解决的方法是在每次开始内层循环时用dx将外层循环cx的值保存起来,在执行外层循环的loop指令前再回复外层循环的cx的数值。
- 改进后程序
|
|
- 在上面的程序中,8086 CPU si、cx、ax、bx这些寄存器经常要使用到;cs、ip、ds也不能用,因为cs:ip时刻指向当前指令,ds指向datasg段;那么可用的寄存器就只用dx、di、es、ss、sp、bp等寄存器了。内存可以解决经常性的数据暂存问题。为了使程序结构清晰便于阅读,应该使用栈
- 再次被改进的程序
|
|
- 再次使用栈改进程序
|
|
- 编程将数据段中的每个单词的前四个字母改为大写字母
数据段中的数据存储结构3
|
|
八、数据处理的两个基本问题
引言
- 本章是总结性的内容,数据处理的两个基本问题是
- 处理的数据在哪?
- 要处理的数据有多长?
- 自定义得描述符:
- reg寄存器
- ax、bx、cx、dx、ah、al、bh、bl、ch、cl、dh、dl、sp、bp、si、di;
- sreg段寄存器
- ds、ss、cs、es。
- reg寄存器
8.1 bx、si、di、bp
- 在8086 CPU中只有bx、si、di、bp这四个寄存器用在 中进行内存单元寻址。在[]中,组合只能以这四种形式:bx和si、bx和di、bp和si、bp和di
|
|
- 正确的指令
|
|
- [bp]的段地址默认在ss中。
8.2 机器指令处理的数据所在的位置
- 绝大部分机器指令时进行数据处理的,大致可以分为3类:读、写、运算。指令在处理前可以在三个地方:CPU内部、内存、端口。
机器码 | 汇编指令 | 指令执行前数据的位置 |
---|---|---|
89C3 | mov bx,[0] | 内存,ds:0单元 |
89C3 | mov bx,ax | CPU内部,ax寄存器 |
BB0100 | mov bx,1 | CPU内部,指令缓冲器 |
8.3 汇编语言中数据位置的表达
- 汇编语言中用三个概念来表达数据的位置。
- 1、立即数(idata)
- 2、寄存器
- 3、段地址(SA)和偏移地址(EA)
8.4 寻址方式总小结
寻址方式总结_1
8.5 指令要处理的数据有多长
- 8086 CPU可以处理byte和word两种数据尺寸。
- 通过寄存器指明要处理的数据尺寸;push指令只进行字操作,若没有寄存器名存在的情况下,用操作符word ptr或者byte ptr指明内存单元的长度。 例如
|
|
|
|
8.6 寻址方式的综合应用
86题目_1
- 初步汇编代码
|
|
- c语言描述
|
|
- 按照c语言的风格用汇编写
|
|
- 多种寻址方式为结构化数据的处理提供了方便。
- 一般用[bx+idata+si]的方式来访问结构体,用idata定位结构体中的某一数据项,用si定位数组项中的每个元素。 例如:[bx].idata、[bx].idata[si]。
8.7 div指令
- div(divide)是除法指令,可用乘法模拟,格式为:
|
|
- 除数:8位或16位,在寄存器或内存单元中;被除数:默认放在AX或DX和AX中。
|
|
除数 | 被除数 |
---|---|
8位 | 16为(AX) |
16位 | 32位(DX高16位+AX低16位) |
- 8位或16位看的是除数。
运算 | 8位 | 16位 |
---|---|---|
商 | AL | AX |
余数 | AH | DX |
- 利用除法指令计算10001/100编程
|
|
- 利用除法指令计算100001/100编程
|
|
8.8 伪指令dd
- db定义字节型数据,dw定于字型数据,dd 定于 dword(double word双字型数据)
|
|
- 利用除法指令计算 dd 100001H 除以 dw 100,商放在 dw 0中
|
|
8.9 伪指令dup
- 和db、dw、dd等数据定义伪指令配合使用,用来进行数据的重复。格式 db或者dw或者dd 重复的次数 dup (重复的数据)
- 例如:
|
|
实验七 寻址方式在结构化数据访问中的应用
实验7
实验7数据
- ds已经和data段联系了,数据段不够用时用扩展段ES
|
|
- 完整的程序
|
|
转移指令的原理
引言
- 可以修改IP,或者同时修改CS和IP的指令统称为转移指令。 简单的来说可以控制CPU执行内存中某处代码的指令就是转移指令。
- 8086
- CPU的转移行为有只修改的段内转移(如jmp ax) 和同时修改该CS和IP的段间转移(如jmp 1000:0)。其中段内转移分为短转移(IP的修改范围为-128~127)和近转移 (IP的修改范围为-32768~32767)。
- 8086 CPU的转移指令分为以下几类:
- 无条件转移指令(如:jmp)
- 条件转移指令
- 循环指令(如:loop)
- 过程
- 中断
9.1 操作符offset
- offset是伪指令,由编译器处理,它的功能是取得标号的偏移地址。
|
|
9.2 jmp指令
- jmp为无条件转移,可以只修改IP,也可以同时修改CS和IP。
- jmp需要两种信息
- 1、转移的目的地址;
- 2、转移的距离(段间转移、段内转移、段内近转移)。
9.3 依据位移进行转移的jmp指令
- 段内短转移,jmp short 标号 ,对IP的修改范围是-128~127,一个字节的空间,即向前转移最多128字节,向后最多127字节。short 表明指令进行的是短转移,标号指明了指令要转移的目的地,转移指令结束后CS:IP指向标号处的指令。
|
|
一般汇编指令中的立即数(idata)会出现在对应的机器指令中。而jmp指令的机器指令并不包含目的地址,包含的是相对于当前IP的转移位移,CPU并不需要目的地址就可以实现对IP的修改。
92debug_1
CPU执行指令的过程 在 2.10 CS和IP
jmp short s 指令的读取和执行过程:
- 1、CS:IP指向jmp short s 的机器码;
- 2、读取指令码进入指令缓冲器
- 3、 改变IP,(IP)=(IP)+所读取指令的长度,IP指向下一个指令;
- 4、CPU执行指令缓冲器中的指令;
- 5、执行后CS:IP继续指向下一个指令
jmp short 标号的功能为(IP)=(IP)+8位位移。
- 1、8位为=标号处的地址-jmp指令后的第一个字节的地址;
- 2、short 指明此处的位移为8位;
- 3、8位位移的范围为-128~127,用补码表示。
- 4、8位位移由编译程序在编译时算出的。
jmp near ptr 标号 指令实现段内近转移,功能为(IP)=(IP)+16位位移。
- 1、16位为=标号处的地址-jmp指令后的第一个字节的地址;
- 2、nearptr 指明此处的位移为16位;
- 3、16位位移的范围为-32769~32767,用补码表示。
- 4、16位位移由编译程序在编译时算出的。
转移位移的计算方法
9.4 转移的目的地址在指令中的jmp指令
- jmp far ptr 段间转移,又称为远转移
- jmp far ptr 标号的功能:
- (CS)=标号所在段的段地址;
- (IP)=标号所在段总的偏移地址;
- far ptr 指明了指令用标号的段地址和偏移地址修改CS和IP。
|
|
- 机器码中包含了转移的目的地址。
94debug
附注3 汇编编译器(masm.exe)对jmp的相关处理
这里写图片描述
9.5 转移地址在寄存器中的jmp指令
- jmp 16位寄存器,功能是16位寄存器赋值给IP,实现段内的近(短)转移。
- 参考 2.11 修改CS、IP的指令
9.6 转移地址在内存中的jmp指令
转移地址在内存中的jmp指令有两种格式:
- 1、jmp word ptr内存单元地址(16位只能实现段内转移)。 功能是从内存单元地址处开始存放一个字(转移的目的偏移地址),内存单元地址可用寻址方式的格式给出。
1 2 3 4 5 6 7 8 9
mov ax,0123H mov ds:[0],ax jmp word ptr ds:[0] ;相当于 jmp ax,执行后(IP)=0123h mov ax,0123H mov [bx],ax jmp word ptr [bx] ;执行后(IP)=0123h
- 2、jmp dword ptr 内存单元地址(段间转移)。 功能:从内存单元地址处开始存放两个字型数据,高地址是转移的目的段地址,低地址处是转移的目的偏移地址。(CS)=(内存单元地址+2),(IP)=(内存单元地址),内存单元地址可用寻址方式的任一格式给出。
1 2 3 4 5 6 7 8 9 10 11
mov ax,0123H mov ds:[0],ax mov word ptr ds:[2],0 jmp dword ptr ds:[0] mov ax,0123H mov [dx],ax mov word ptr [bx+2],0 jmp dword ptr [bx] ;执行后 (CS)=0,(IP)=0123H CS:IP指向0000:0123
检测点 9.1
检测点 9.1
9.7 jcxz指令
- 指令格式为jcxz 标号,如果cx的值为0,则转移到标号处执行,不为0则向下执行。
- 当cx的值为0时,(IP)=(IP)+8位位移,8位位移=标号处的地址-jcxz指令后的第一个字节的地址。
- 8位位移的范围是-128~127,用补码表。
- 8位位移由编译器在编译时算出。
- jcxz指令是有条件转移指令,所有的条件转移指令都是短指令,在对应的机器码中包含转移的位移而不包含目的地址,对IP的修改范围都为-128-127。
检测点 9.2
检测点9.2_1
9.8 loop指令
- **loop指令为循环指令,所有的循环指令都是短转移,在对应的机器码中包含转移的位移而不包含目的地址。**操作i:
- cx先自减1;
- 当cx的值不为0时,(IP)=(IP)+8位位移,8位位移=标号处的地址-loop指令后的第一个字节的地址。
- 8位位移的范围是-128~127,用补码表。
- 8位位移由编译器在编译时算出。
检测点 9.3
检测点 9.3
9.9 根据位移进行转移的意义
|
|
- 它们对IP的修改时根据转移目的地址和转移起始地址自检的位移来进行的。在它们对应的机器码中不包含转移的目的地址,而包含的是目的地址的位移距离。方便了程序段在内存中的浮动分配,没有固定目的地址的限制,更灵活。
9.10 编译器对转移位移超界的检测
- 根据位移进行转移的指令,它们的转移范围受到了转移位移的限制,如果在源程序中出现了转移范围超界的问题,在编译时编译器会报错。
|
|
910err
910err2
实验8
实验八可以正常退出
test8
|
|
test8debug
实验9
实验9
|
|
- welcome to masm
welcome to masm
十、CALL和RET指令
引言
- 回想程序之间的加载返回过程。
- call和ret指令都是转移指令,它们都修改IP或者同时修改CS和IP,经常被共用来实现程序的设计。
- 这一章讲解call和ret指令的原理。
10.1 ret和retf指令
- ret指令用栈中的数据来修改IP的内容,从而实现近转移。
- CPU执行ret指令时:
- 1、(IP)=((SS)*16+(SP)),指向栈顶
- 2、(SP)=(SP)+2
- retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移。
- CPU执行retf指令时,进行下面两步操作:
- 1、(IP)=((SS)*16+(SP))
- 2、(SP)=(SP)+2
- 3、(CS)=((SS)*16+(SP))
- 4、(SP)=(SP)+2
- 用汇编的语法来解释ret和retf指令:
- CPU执行ret指令相当于进行 POP IP
- CPU执行retf指令相当于进行 POP IP和POP CS
|
|
1012debug_1
|
|
检测点 10.1
检测点10.1
10.2 call指令
- call指令经常跟ret指令配合使用,CPU执行call指令时:
- 1、将当前的IP或者CS和IP压入栈;
- 2、转移(jmp)。
- call指令除了不能实现短转移之外,call指令实现转移的方法和jmp指令的原理相同。call指令实现段间的转移(远转移)或近转移。
10.3 依据位移进行转移的call指令
- call标号(将当前的IP压入栈后转到目标处执行指令),执行时进行以下操作:
- 1、(SP)=(SP)-2
((SS)*16+(SP))=(IP) - 2、(IP)=(IP)+16位位移;
- 3、16位位移=标号处的地址减去call指令后的第一个字节的地址。16位位移的范围是-32768~32767,用补码表示。16位位移由编译器编译时算出。
- 1、(SP)=(SP)-2
- 用汇编语法解释call指令:
|
|
检测点 10.2
检测点10.2
10.4 转移的目的地址在指令中的call指令
- call far ptr 标号 实现的是段间转移,执行时:
- 1、CS先自减2;
- 2、CS的值等于SS的值乘以16加上SP的值,SP自减2,IP的值等于SS的值*16加上SP的值;
- 3、CS的值等于标号所在的段地址,IP的值等于标号所在的偏移地址.
- 用汇编语法解释call指令:
|
|
检测点 10.3
检测点 10.3
10.5 转移地址在寄存器中的call指令
- 指令格式是:call 16位寄存器,功能是:
- 1、SP的值先自减2;
- 2、IP的值SS的值乘以16再加上SP的值;
- 3、 IP的值等于16位寄存器的内容。
- 用汇编语法解释此种call指令,CPU执行call 16位reg时,相当于:
|
|
检测点 10.4
检测点 10.4
10.6 转移地址在内存中的call指令
- 转移地址在内存中的call指令有两种格式
|
|
- 用汇编语法解释call word ptr 内存单元地址
|
|
- 例子:
|
|
- 用汇编语法解释call dword ptr 内存单元地址
|
|
- 例子:
|
|
检测点 10.5
检测点10.5
10.7 call和ret的配合使用
- 下面的程序返回前,bx中的值是多少?
|
|
- 具有一定功能的程序段称为子程序,用call转去执行,在子程序后面使用ret实现返回。
- 具有子程序的源程序的框架如下
107call
10.8 mull指令
- mull指令时乘法指令,相乘的两个数要么都是8位的,要么都是16位的
- 8位:在AL中和8位寄存器中或内存字节单元中;
- 16位:在AX中和16位寄存器或内存字单元中。
- 结果
- 8位的存放在AX中;
- 16位:DX(高位)和AX(低位)中。
|
|
|
|
10.9 模块化程序设计
- cal和ret指令共同支持汇编语言编程中的模块化设计。
10.10 参数和结果传递的问题
用寄存器来存储参数和结果是最常用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反:
- 调用者将参数送入参数寄存器,从结果寄存器中取到返回值;
- 子程序 从参数寄存器中取到参数,将返回值送入结果寄存器。
编程:根据提供的N来计算N^3
|
|
- 编程:计算data段中第一组数据的3次方,结果保存在后面一组dword单元中
|
|
10.11 批量数据的传递
- 将批量数据放在内存中,然后将他们呢所在内存空间的首地址放在寄存器中,传递给需要的子程序,批量数据的返回结果也是采用同样的方法。除此之外还可以用栈来传递参数。
|
|
10.12 寄存器冲突的问题
- 编程:将一个全是字母,以0结尾的字符串转化为大写
|
|
- 编程将data段中的字符串全部转化为大写
|
|
|
|
实验十
test10_1_1
test10_1_2
test10_1_3
- 实验10.1 显示字符串
|
|
test101
- 实验10.2
|
|
- 实验10.3
|
|
十一、标志寄存器
引言
- CPU内部的寄存器中有一种特殊的寄存器:
- 1、用来存储相关指令的某些执行结果;
- 2、用来为CPU执行相关指令提供行为依据;
- 3、用来控制CPU的相关工作方式。
- 8086 CPU的标志寄存器只有16位,其中存储的信息通常被称为程序状态字(PSW)。
- 本章中的标志寄存器(以下简称为flag)。某些指令将影响标志寄存器中的多个标志位,这些被影响的标记位比较全面地记录ill指令的执行结果,为相关的处理提供了所需的依据。
- flag寄存器是按位起作用的,每一位都有专门的含义,记录特定的信息,与其他寄存器不一样。
- 8086 CPU的flag寄存器的结构:
flag
- flag的1、3、5、12、13、14、15位在8086 CPU中没有使用,而0、2、4、6、7、8、9、10、11位都具有特殊的含义。
11.1 ZF(zero flag)标志
- flag的第6位是ZF,零标志位,它记录相关指令执行后,结果为0,ZF=1(记录下是0这样的肯定信息),结果不为0,ZF=0(表示结果非0)。
|
|
- 在8086CPU中,add、sub、mul、div、inc、or、and等它们大多都是运算(逻辑运算或是算术运算)指令,是影响标志寄存器的,而mov、push、pop等传送指令对标志寄存器一般没有影响,因为不会产生结果。
11.2 PF标志
- flag的第2位是PF,奇偶标志位,记录指令执行后结果所有的二进制位中1的个数。为偶数,PF=1,为奇数PF=0
|
|
11.3 SF(sign flag)标志
- flag的第7位是SF符号标志位,记录指令执行后结果为负则SF=1,结果为正,SF=0。弱国我们将数据当作无符号数来运算,SF的值没有意义,虽然相关的指令影响了它的值。
- 有符号数与补码
- 计算机默认把负数用补码记录。
- 00000001B,可以看作无符号数1,也可以看作符号数+1;
- 10000001B,可以看作无符号数129,也可以看作有符号数-127。
- 补码
|
|
检测点 11.1
检测点11.1
11.4 CF(carry flag)标志
flag的第0位是CF,进位标志位。一般情况下,在进行无符号运算的时候,它记录了运算结果的最高有效位向更高位的进位值或从更高位的借位值。对于位数为N的无符号数,其对应的二进制信息的最高位为N-1位的最高有效位,假想存在第N位。
更高位
两个8位的数据运算可能产生进位或者借位,由于这个进位值在8位数中无法保存,8086CPU就用flag的CF位来记录这个进位值。
|
|
11.5 OF(overflow flag)标志
- 如果运算结果超出了机器所能表达的范围(对于8位有符号数,机器所能表达的范围是-128~127)将产生溢出,对有符号数而言。
|
|
1150f_1
|
|
115of2
|
|
CFdebug
|
|
114cf2
- CF是对无符号数运算有意义的标志位,而OF是对有符号数运算有意义的标志位; CPU用CF位来记录无符号数运算是否产生了进位,用OF位来记录有符号数是否产生了溢出。用SF位来记录结果的符号
|
|
检测点 11.2
检测点11.2
11.6 adc指令
- adc是带有进位加法指令,利用了CF位上记录的进位值。格式:adc操作对象1,操作对象2,功能:操作对象1=操作对象1+操作对象2+CF。
|
|
- 由adc指令前面的指令决定在执行adc指令的时候加上的CF的值的含义,关键在于所加上的CF的值是被什么指令设置的。如果CF的值是被sub指令设置的,那么它的含义就是借位值;如果是被add指令设置的,那么它的含义就是进位值。加法运算先是低位相加,再高位相加加上低位相加产生的进位值。
- 编程:计算1EF000H+201000H,结果存放在AX(高16位)和BX(低16位)中。
|
|
- 编程:1EF0001000H+2010001EF0H,结果存放在AX(高16位)、BX(次16位)中和cx(低16位)。
|
|
- 编程:对两个128位数据进行相加
|
|
11.7 sbb指令
- sbb是带借位减法指令,利用了CF位上记录的借位值。格式:sbb 操作对象1,操作对象2,功能是:操作对象1=操作对象1-操作对象2-CF。
- 利用sbb指令我们可以对任意大的数据进行减法运算。sbb和adc是基于同样的思想设计的两条指令,在应用思路上sbb和adc类似。
- 编程:计算003E1000H-00202000H,结果放在ax、bx中
|
|
11.8 cmp指令
- cmp是比较指令,功能上相当于减法指令,只是不保存结果。格式:cmp 操作对象1,操作对象2.功能:计算操作对象1-操作对象2但不保存结果,仅仅是根据计算结果对标志寄存器进行设置。
- cmp指令运算执行后通过做减法将对标志寄存器产生影响,其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
|
|
|
|
118cmp_1
118cmp2
- CPU在执行cmp指令时也包含了对无符号数运算和进行有符号数运算,所以利用cmp指令可以对无符号数进行比较也可以对有符号数进行比较。
- 单纯地考察SF的值不可能知道结果的正负。因为SF记录的只是可以在计算机中存放的相应位数的结果的正负(例如:add ah, al执行后,SF记录的是ah中的8位二进制信息所表示的数据的正负)。如果没有溢出发生的话,实际结果的正负和逻辑上真正结果的正负就一致了。。例如:22H(34)-0A0H(-96)=130=82H(是-126的补码),SF=1。
- 1、如果SF=1或SF=0,OF=0,逻辑上真正结果的正负=实际结果的正负。
- 2、如果SF=1或SF=0,OF=1,逻辑上真正结果的负正=实际结果的正负。
11.9 检测比较结果的条件转移指令
- 与cmp相配使用,根据cmp指令的比较结果(cmp指令执行后相关标志位的值)进行工作的指令。
- cmp指令可以同时进行两种比较,无符号数比较和有符号数比较,所以根据cmp指令的比较结果进行转移的指令也分为两种:
- 根据无符号数的比较结果进行转移的条件转移指令,它们检测ZF、CF的值;
- 根据有符号数的比较结果进行转移的条件转移指令,它们检测SF、OF、ZF的值。
- 它们所检测的标志位都是cmp指令进行无符号数比较时候记录比较结果的标志位。
指令 | 含义 | 检测的相关标志位 |
---|---|---|
je | 等于则转移 | ZF=1 |
jne | 不等于则转移 | ZF=0 |
jb | 低于则转移 | CF=1 |
jnb | 不低于则转移 | CF=0 |
ja | 高于则转移 | CF=0 and ZF=0 |
jna | 不高于则转移 | CF=1 or ZF=1 |
j | e | ne | b | nb | a | na |
---|---|---|---|---|---|---|
jump | equal | not equal | below | not below | above | not above |
- 编程:如果ah的值等于bh则ah的值等于ah的值加ah的值,否则ah的值等于ah的值加上bh的值。
|
|
- je检测的是ZF的位置,不管je前面是什么指令,只要CPU执行je指令时,ZF=1那么就发生转移。
|
|
课堂练习
- 编程:统计data段中数值为8的字节的个数,用ax保存统计结果。
|
|
|
|
- 编程:统计data段中数值大于8的字节的个数,用ax保存统计结果。
|
|
检测点 11.3
检测点11.3
11.10 DF(direction flag)标志和串传送指令
- flag的第10位是DF,方向标志位,在串处理指令中,控制每次操作后si(一般指向原始偏移地址)、di(一般指向目标偏移地址)的增减。
- DF=0:每次操作后si、di递增;
- DF=1,每次操作后so、di递减。
- movsb(mov string byte)串传送指令,以字节为单位传送。将ds:si指向的内存单元中的字节送入es:di中,然后根据标志寄存器DF位的值将si和di递增1或递减1。movsw,以字为单位传送。将ds:si指向的内存单元中的字送入es:di中,然后根据标志寄存器DF位的值将si和di递增2或递减2。
- **movsb和movsw进行的是串传送操作中的一个步骤,一般和rep配合使用,格式:rep movsb,rep的作用是根据cx 的值,重复执行后面的串传送指令。**由于每次执行一次movsb指令si和di都会递增或递减指向后一个单元或前个单元,则rep movsb就可以循环实现(cx)个字符的传送。
- 1、传送的原始位置;
- 2、传送的目的位置;
- 3、传送的长度;
- 4、传送的方向。
- movsb功能:((es)*16+(di))=((ds)*16+(si)),如果DF=0,则(si)=(si)+1,(di)=(di)+1;如果DF=1,则(si)=(si)-1,(di)=(di)-1。
- 由于flag的DF位决定着串传送指令执行后,si和di改变的方向,8086CPU提供两条指令对DF位进行设置:
- cld指令:将标志寄存器的DF位设置为0;
- std指令:将标志寄存器的DF位设置为1。
11.11 pushf和popf
- pushf的功能 是件标志寄存器的值压栈,popf是从栈中弹出数据m,送入标志寄存器中。pushf和popf为直接访问标志寄存器提供了一种方法。
|
|
- 编程:用串传送指令将data段总的第一个字符串复制到它后面的空间中。
|
|
1110
- 用串传送指令将F00H段中的最后16个字符复制到data段中
|
|
检测点 11.4
检测点11.4
11.12 标志寄存器在Debug中的表示
debugflag
标志 | 值为1的标记 | 值为0的标记 |
---|---|---|
OF | OV | NV |
SF | NG | PL |
ZF | ZR | NZ |
PF | PE | PO |
CF | CY | NC |
DF | DN | UP |
十二、内中断
引言
- 中断时CPU处理外部突发事件的一个重要技术。它能使CPU在运行过程中对外部事件发出的中断请求几时进行处理,处理完成后又立即返回断电,基础进行CPU原来的工作。引起中断的原因或是说发出中断请求的来源叫做中断源。根据中断源的不同,可以把中断分为硬件中断和软件中断两大类,而硬件中断又可以分为外部中断和内部中断两类。
- 外部中断一般是指由计算器外部设备发出的中断请求。如:键盘中断、打印机中断、定时器中断等。外部中断时可以屏蔽的中断,业绩是说利用中断控制器可以屏蔽这些外部设备的中断请求。
- 内部中断是指因硬件出错(如突然掉电)或运算出错(如除数为0、单步中断)所引起的中断。内部中断是不可屏蔽的。
- 软件中断其实并不是真正的中断,它们只是可被调用执行的一般程序以及DOS的系统功能调用(int 21)等都是软件中断。
- 中断的优先权:
- 1、除法出错、溢出中断、软件中断;
- 2、不可屏蔽中断;
- 3、可屏蔽中断;
- 4、单步中断。
- 中断信息中包含有标识中断源的类型码。根据CPU的设计,中断源类型码的作用就是用来定位中断处理程序。
12.1 内中断的产生
- 8086CPU内部有以下情况发生时将产生相应的中断信息:
- 1、除法错误;
- 2、单步执行;
- 3、执行into指令;
- 4、执行int指令。
- 8086CPU中的中断类型码如下:
- 1、除法错误:0
- 2、单步执行:1
- 3、执行into指令:4
- 4、执行int指令,该指令格式为int n,n为立即数是提供给CPU的中断类型码。
12.2 中断处理程序
- CPU在收到中断信息后立即去执行该中断信息的处理程序。
12.3 中断向量表
- 中断向量列表就是中断向量(中断处理程序的入口地址)的列表,其在内存中保存,存放着256个中断源说对应的中断处理程序的入口。8086PC机中断向量表放在内存地址0处。从内存0000:0000到0000:03FF的1024(一个物理地址是由段地址和偏移地址构成,要用4个字节来存放)个单元中存放着中断向量表。
- CPU用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。中断向量表中存放的就是各个类型的处理程序的地址,8位的类型码是个索引。
12.4 中断过程
- 用中断码在中断向量表中找到中断处理程序的入口地址,用它来设置CS和IP,使CPU执行中断程序。用中断类型码找到中断向量并用它设置CS和IP,这个工作室由CPU的硬件自动完成的,这个工作的过程被称为中断过程。
- 8086CPU的中断过程:
- 1、从中断信息中取得中断类型码;
- 2、标志寄存器的值入栈,以保护标志位;
- 3、设置标志寄存器的第8位TF和第9位IF的值为0;
- 4、CS的内容入栈,IP的内容入栈;
- 5、从内存地址为中断类型码* 4和中断类型码 *4+2的两个字单元中读取中断处理程序的入口地址设置IP和CS。
- 在最后一步完成后,CPU开始执行由程序员编写的中断处理程序。
12.5 中断处理程序和iret指令
- 常规的步骤
- 1、保存用到的寄存器;
- 2、处理中断;
- 3、 恢复用到的寄存器;
- 4、 用iret指令返回。
- iret指令的功能为相应的按顺序恢复之前保存起来的IP、CS地址和标志位寄存器。用汇编语法描述为:
|
|
12.6 除法错误中断的处理
- 当CPU执行dvi等处罚指令的时候,如果发生了除法溢出错误,将产生中断类型码为0的中断信息,CPU将检测到这个信息然后引发中断过程,转去执行0号中断所对应的中断处理程序。
|
|
126
debug 126
12.7 编程处理0号中断
- 改变0号中断处理程序的功能,在屏幕中间显示字然后再返回操作系统。
- 当发生除法溢出时产生0号中断信息,引发中断过程。
- 此时CPU将进行以下工作(中断过程)
- 当中断0发生时,CPU将转去执行中断处理程序。
- 先进行相关处理,然后向显示缓冲区送字符串,最后返回。
- 改变后的中断处理程序应该放在内存中,因为除法溢出随时可能发生,CPU随时都可能将CS:IP指向改变后的中断处理程序的入口执行程序。
- 把程序存入内存,修改向量表(即将内存地址登记在中断向量表的对应表项中),中断时调用这个内存。
- 当发生除法溢出时产生0号中断信息,引发中断过程。
除法溢出对应的中断类型码为0,它的中断处理程序的入口地址应该从0* 4+2地址单元开始存放,段地址存放在0* 4+2字单元中,偏移地址存放在0*4字单元中。也就是改变后的中断处理程序的段地址0存放在0000:0002字单元中,偏移地址200H存放在0000:0000字单元中。如果要显示的字符串在程序的data段中,那么程序执行完成后返回,它所占用的内存空间被系统释放,在其中存放的信息也可能被别的信息覆盖。
|
|
do0
do02
12.8 单步中断
- CPU执行完一条指令之后,如果检测到标志寄存器的TF位为1,则产生单步中断引发中断过程。单步中断的中断类型码为1,它所引发的中断过程如下:
- 1、取得中断类型码;
- 2、标志寄存器入栈,TF、IF设置为0;
- 3、CS、IP入栈;
- 4、指向指定类型码的中断向量表。
12.9 响应中断的特殊情况
- 在有些情况下CPU在执行完当前指令后,即便是发生了中断也不会响应。
在执行完向ss寄存器传送数据的指令后,即便检测到了中断信号CPU也不会响应。因为ss:sp指向栈顶,对他们的设置应该连续完成。如果在执行完设置ss指令后mCPU响应中断引发中断过程,要在栈中压入标志寄存器、CS和IP的值。而ss改变,sp并未改变则ss:sp指向不是正确的栈顶将引发错误。
- 我们要将栈顶设置为1000:0,不应该隔开
应该 | 不应该 |
---|---|
mov ax,1000h | mov ax,1000h |
mov ss,ax | mov ss,ax |
mov sp,0 | mov ax,0 |
mov ax,0 | mov sp,0 |
十三、int 指令
引言
- 在第12章中了解中断过程和除法错误中断和单步中断的处理,这章了解int指令。
13.1 int 指令
int格式:int n,n为中断类型码,它的功能是引发中断过程。CPU执行int n之力量能够,相当引发一个n号的中断过程,可以在程序中使用int指令调用任何一个中断的中断处理程序。执行过程如下:
- 中断过程从,此处去执行n号中断的中断处理程序。
1 2 3 4 5 6 7 8 9 10 11 12
assume cs:code code segment start: mov ax,0b800h mov es,ax mov byte ptr es:[12*160+40*2],'!' int 0;执行int 0指令,引发中断过程,执行0号中断处理程序 code ends end start
131dosbox
int指令的最终功能和call类似,都是调用一段程序。一般情况下系统将一些具有一定功能的子程序以中断处理程序的方式提供给应用程序调用,也可以自己编写一些中断处理程序供别人使用。
13.2 编写供应用程序调用的中断例程
- 中断处理程序简称为中断例程。
- 实例1:编写、安装中断7ch的中断例程实现求一word型数据的平方。
- 1、编程实现求平方功能的程序;
- 2、安装程序在0:200处;
- 3、设置中断向量表将程序的入口地址保存在7ch表项中,使其成为中断7ch的中断例程。
|
|
- CPU执行int 7ch指令进入中断例程之前,标志寄存器、当前的CS和IP都被压入栈中,在执行完中断例程后,用iret指令恢复int 7ch执行前的标志寄存器和CS和IP的值,从而接着执行应用程序。
- int指令和iret指令配合使用与call指令和ret指令配合使用具有相似的思路。
- 实例2:编写、安装中断7ch的中断例程,实现将一个全是字母,以0为结尾的字符串转化为大写。
|
|
- 要注意用到的寄存器冲突。
13.3 对int、iret和栈的深入理解
- 中断处理程序和iret指令
- 编程:用7ch中断例程完成loop指令的功能,在屏幕中间显示80个"!".
loop指令需要循环次数和到标号的位移。为了模拟loop指令7ch中断例程应具备下面dec cx和如果cx的值不等于0则转移到标号s处。
|
|
|
|
|
|
136
13.7 dos中断例程应用
- int 21h 中断例程
|
|
int 2h中断例程还具有在光标位置显示字符串的功能、
1 2 3
ds:dx;要显示的字符串需要用 $ 作为结束符 mov ah,9;功能号9,表示在光标位置显示字符串 int 21h
十四、端口
引言
- CPU可以直接读写3个地方的数据:
- 1、CPU内部的寄存器;
- 2、内存单元;
- 3、 端口。
14.1 端口的读写
- mov、push、pop等死内存读写指令。in和out是端口读指令写指令时in是从端口读取数据,out是往端口写入数据。in和out指令只能用ax或al来存放从端口中读入的数据或要发送到端口中的数据,访问8位短空时用al,访问16位端口时用ax。
- 访问内存:
- mov ax,ds:[8+0];假设(ds)=0
- 执行时,与总线相关的操作:
- 1、CPU通过地址线信息8发出;
- 2、CPU通过控制线发出内存读命令,选中存储器芯片并通知它将要从中读取数据;
- 3、 存储器将8号单元中的数据通过数据线送入CPU。
- 访问端口:
- in al,60h;从60h号端口读入一个字节。
- 执行时与总线相关的操作:
- 1、CPU通过地址线将地址信息60h发出;
- 2、CPU通过控制线发出端口读命令,选中端口所在的芯片,并通知它,将要从中读取数据;
- 3、端口所在的芯片将60h端口中的数据通过数据线送入CPU。
- 访问内存:
|
|
14.2 CMOS RAM芯片
CMOA RAM特征:
1、包含一个实时钟和一个有128个存储单元的RAM存储器。(早期的计算机位64个字节)。
2、该芯片靠电池供电。因此关机后其内部的实时钟仍可正常工作,RAM中的信息不会丢失。
3、128个字节的RAM,内部实时钟占用0~0dh单元来保存时间信息默契与大部分单元用于保存系统配置信息,供系统启动时bios程序读取。
bios也提供了相关的程序使用户在开机时配置CMOS RAM中的系统信息。
4、该芯片内部有两个端口,端口地址为70h和71
h。CPU通过这两个端口读写CMOS RAM。5、70h为地址端口,存放要访问的CMOS RAM单元的地址;71h为数据端口,存放从选定的CMOS RAM单元中读取的数据,或要写入到其中的数据。
CPU对CMOS RAM的读写分两步进行,以读2号单元为例:
- 1、将2送入端口70h;
- 2、从71h读出2号单元的内容。
14.3 shl和shr指令
- shl为逻辑左移指令功能为:
- 1、将一个寄存器或内存单元中的数据向左移位;
- 2、将最后移出的一位写入CF中;
- 3、最低位用0补充。
|
|
- 如果移动位数大于1时,必须将移动位数放在cl中。
|
|
- 二进制逻辑左移一位,相当于执行x=x*2(2是进制位)
mov al,00000001b | 执行后al的值等于00000001b=1 |
---|---|
shl al,1 | 执行后al的值等于00000010b=2 |
shl al,1 | 执行后al的值等于00000100b=4 |
shl al,1 | 执行后al的值等于00001000b=8 |
mov cl,3 | |
shl al,cl | 执行后al的值等于01000000b=64 |
- shr为逻辑左移指令功能为:
- 1、将一个寄存器或内存单元中的数据向右移位;
- 2、将最后移出的一位写入CF中;
- 3、最高位用0补充。
- 二进制逻辑右移一位,相当于执行x=x/2(2是进制位)
14.4 CMOS RAM中存储的时间信息
在CMOS RAM中以每个信息一字节存放着当前的时间信息:年09h,月08h,日07h,时04h,分02h,秒00h。这些数据以BCD码的方式存放,BCD码以4位为一位。
数值26BCD码表示为0010 0110,用两个BCD码表示两位十进制,高4位表示十位,低4位表示各位。
编程:在屏幕中间显示当前的月份。
|
|
144_1
十五、外中断
15.1接口芯片和端口
- CPU通过端口和外设进行联系。在PC系统的接口卡和主板上,装有各种接口芯片。这些外设接口芯片的内部有若干寄存器,CCPU将这些寄存器当作端口来访问。外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中;CPU向外设的输出也不是直接送入到外设而是先送入端口再由相关的芯片送到外设。
15.2外中断信息
外中断源有两类:
- 1、可屏蔽中断;
- 可屏蔽中断时CPU可以不响应的外中断。CPU是否响应可屏蔽中断要看标志寄存器的IF位的设置。
- 2、不可屏蔽中断
- 1、可屏蔽中断;
当CPU检测到可屏蔽中断信息时:
- 如果IF=1,则CPU在执行完当前指令后响应中断引发中断过程。
- 如果IF=0,着不响应可屏蔽中断。
- 内中断过程
可屏蔽中断所引发的中断过程,除在第一步的实现上有所不同外,基本上和内中断的中断过程相同。因为可屏蔽中断信息来自于CPU外部,中断类型码是通过数据总线送入CPU的;而内中断的中断类型码是在CPU内部产生的。在中断过程中将IF置0的原因是在进入中断处理程序后禁止其他的可屏蔽中断。
8086CPU提供的设置IF的指令如下:
- sti,设置IF=1;
- cli,设置if=0.
不可屏蔽中断是CPU必须响应的外中断。当CPU检测到不可屏蔽中断信息时,则在执行完当前指令后立即响应引发中断过程。对于8086CPU不可屏蔽的中断类型码固定为2。所以中断过程中不需要取中断类型码。几乎所有外设引发的外中断都是可屏蔽中断。
不可屏蔽中断过程:
- 1、标志寄存器入栈,IF=0,TF=0’
- 2、CS和IP入栈;
- 3、(IP)=(8),(CS)=(0AH)
15.3PC机及键盘的处理过程
键盘输入的处理过程:
- 1、键盘输入产生扫描码;
- 2、扫描码送入60h端口;
- 3、引发9号中断;
- 4、执行int 9中断例程。
前三步由硬件系统自动完成,第四步用户可以修改int 9中断程序。
按下一个键产生的扫描码称为通码,松开一个键产生的扫描码称为断码。扫描码被送入主板上的相关接口芯片端口地址为60h的寄存器中。
扫描码长度为一个字节,通码的第7位为0,断码的第7位为1。即断码=通码+80h。
键盘上部分键的扫描码
- bios提供了int 9中断例程,用来进行基本键盘输入处理,主要的工作如下:
- 1、读出60h端口中的扫描码;
- 2、如果是字符键的扫描码就将它和它所对应的字符码(ASCII码)送入内存中的bios键盘缓冲区;
- 键盘的输入到达60h端口时相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息。
- CPU检测到该中断信息后,如果IF=1,则相应中断,引发中断过程,转去执行int 9中断例程。
- 如果是控制键(如ctrl)和切换键(如capslock)的扫描码,则将其转变为状态字节(用为进制位记录控制键和切换键状态的字节)写入内存中存储状态字节的单元
- 3、键盘系统进行相关的控制。如向相关芯片发出应答信息。
- bios键盘缓冲区是系统启动后mbios用于存放int 9中断例程所接收的键盘输入的内存区。该内存可以存储15个键盘输入,在bios键盘缓冲区中一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码。0040:17单元存储键盘状态字节该字节记录了控制键和切换键的状态
- 0:置1表表示按下右shift键
- 1:置1表表示按下左shift键
- 2:置1表表示按下ctrl
- 3:置1表表示按下alt
- 4:置1表表示按下scroll指示灯亮
- 5:置1表表示按下numlock,小键盘输入的是数字
- 6:置1表表示按下capslock,输入大写字母
- 7:置1表表示按下insert。处于删除状态
15.4编写int 9中断
- 键盘输入的处理过程
- 编程:在屏幕中间依次显示让人看清的a~z,按下esc键后改变显示的颜色。
|
|
|
|
|
|
- int指令在执行时CPU进行的工作
- 完整程序
|
|
15.5安装新的int 9中断例程
- 小甲鱼版(笔者未成功运行)
|
|
- 王爽原版(笔者未成功运行)
|
|
第16章 直接定址表
16.1 描述单元长度的标号
|
|
在code段中使用的标号a,b后面没有:,因此他们可以同时描述内存地址和单元长度的标号
|
|
检测点 16.1
检测点16.1
16.2 在其他段中使用数据标号
- 注意:在后面加有:的地址标号只能在代码段中使用,不能在其他段中使用。
|
|
- 如果现在代码段中直接用数据标号访问数据,则需要用伪指令assume将标号所在的段和一个段寄存器联系起来。 我们可以将标号当作数据来定义,此时编译器将标号所表示的地址当作数据的值。
|
|
16.3 直接定址表
利用表,在两个数据集合之间建立一种映射关系,使我们可以利用查表的方法根据给出的数据得到其在另一集合中对应数据
- 目的:
- 为了算法的清晰和简洁
- 为了加快运算速度
- 为了使程序易于扩充
- 目的:
小练习,编写子程序,以十六进制的形式在屏幕中间显示给定的byte型数据。小技巧,利用映射关系,0-9数值+30h=对应字符的ascii值,10-15和A到F之间的银色关系是:数值+37h=对应字符的ascii的值
|
|
16.4 程序入口地址的直接定址表
- 小练习
- 清屏:将显存中当前屏幕中的支付设为空格;
- 设置前景色:设置显存中当前屏幕中处于奇地址的属性字节的第0、1、2位;
- 设置背景色:设置显存中当前屏幕中处于奇地址的属性字节的第4、5、6位;
- 向上滚动一行:依次将第n+行的内容复制到第n行处,最后一行为空。
|
|