IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 8086CPU(汇编语言,王爽) -> 正文阅读

[嵌入式]8086CPU(汇编语言,王爽)

前言

王爽《汇编语言》第四版笔记。

推荐一个up主的配套讲解:
https://www.bilibili.com/video/BV1mt411R7Xv?p=143&spm_id_from=pageDriver

Debug

环境:DOSBox 0.74

在这里插入图片描述

寄存器

全部寄存器:

AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW

共14个。8086CPU的所有寄存器都是16位的。8086CPU是16位结构,有16根数据线。
在这里插入图片描述

通用寄存器

AX、BX、CX、DX

每个可分为 ~H 与 ~L,如 AH 和 AL ,H 表示高八位,L 表示低8位。
在这里插入图片描述

CX和LOOP

在这里插入图片描述
在这里插入图片描述

二重循环问题的处理

P157

因为LOOP指令都在cx中存放循环次数,因此二重循环时我们每次应当将外层的cx保存起来,而寄存器是有限的,所以更通用的方式是将cx的值保存在一个内存单元中;一般来说,在需要暂存数据的时候,我们都应该使用栈;于是我们可以在栈段中单独开辟一个字来暂存cx的值。

ah 与 中断

在这里插入图片描述
比如 BIOS 提供的 int 10h 中断例程(其中包含了多个和屏幕输出相关的子程序,P259):
在这里插入图片描述
DOS 提供的 int 21h 中断程序:
在这里插入图片描述

段寄存器

CS、DS、SS、ES

8086CPU不支持将数据直接送入段寄存器。

如把 al 的数据送入内存单元 10000H 中:

mov bx,1000H
mov ds,bx
mov [0],al

把 1000:0 处存放的字型数据送入ax:

mov bx,1000H
mov ds,bx
mov ax,[0]

小端模式(Little-Endian),数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,所以(以1123H为例,假设该字型数据为 1123H):
1000:1 单元存放字型数据的高8位(11H),1000:0 单元存放字型数据的低八位(23H)。

DS和[address]

8086CPU中的DS寄存器,通常用来存放要访问数据的段地址。 […]则表示一个内存单元,如 [1] 为内存单元: DS:1

段前缀

汇编源程序中,如果在 [] 里用一个常量 idata 直接给出内存单元的偏移地址,就要在 [] 的前面显示地给出段地址所在的段寄存器。例如:
mov al, [0]mov al,0 等价,要表示地址偏移应该写为:mov al,ds:[0],即段前缀的方式:
在这里插入图片描述
或写为( [] 中用寄存器):

mov bx,0
mov al,[bx]

CS和IP(指令)

CS:代码段寄存器
IP:指令指针寄存器
在这里插入图片描述

SS和SP(栈)

任意时刻,SS:SP 指向栈顶元素。 push和pop指令执行时,CPU从 SS 和 SP 中得到栈顶的地址。

栈,LIFO(Last In First Out,后进先出)。8086CPU中,入栈时,栈顶从高地址向低地址方向增长。

push 指令

在这里插入图片描述

栈空间

在这里插入图片描述
如果 pop 让栈顶超界了,再用push指令就会覆盖原本不属于我们划定的栈空间的内存。我们当然希望 CPU 可以解决,比如额外搞两个寄存器去记录栈顶上限和下限,每次pop和push的时候进行检测来保证不越界。

但是8086CPU不保证我们对栈的操作不会超界。也就是说,8086CPU只知道栈顶在何处(由SS:SP指示),而不知道我们安排的栈空间有多大。所以只能靠我们自己编程时小心了。

注意,push、pop 等栈操作指令,修改的只是 SP 。也就是说,栈顶的变化范围最大为:0~FFFFH.

(初始时栈空,SP指向栈的最底部单元下面的单元,地址为栈最底部的字单元的地址+2. SP16位,2^16 B = 2^6 KB,所以一个栈段的容量最大为 64 KB)

pop 指令

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

段的综述

在这里插入图片描述在这里插入图片描述

SI 和 DI

si 和 di 是8086CPU 中和 bx 功能相近的寄存器,si 和 di 不能够分成两个 8 位寄存器来使用。

我们用 [bx(si 或 di)] 和 [bx(si 或 di) + idata] 的方式来指明一个内存单元,我们还可以用更为灵活的方式:[bx + si] 和 [bx + di],[bx + si + idata] 和 [bx + di + idata]

mov ax, [bx + si] 还可以写为 mov ax, [bx][si] ,且以下这几种都和 mov ax, [bx + si + 200]等价:
在这里插入图片描述

bx、si、di 和 bp

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
即 bx 的默认段地址为 ds,bp 则为 ss,若 [] 中有寄存器,则必有 bx 或者 bp (作为基址)

标志寄存器 flag

在这里插入图片描述
在这里插入图片描述

ZF 标志

Zero Flag,零标志位
在这里插入图片描述
比如:mov ax,1 sub ax,1 的结果为0,ZF就为1

PF 标志

Parity Flag,奇偶标志位
在这里插入图片描述

SF 标志

Sign Flag,符号标志位,对有符号数运算结果的一种记录
在这里插入图片描述
在这里插入图片描述

CF 标志

Carry Flag,进位/借位 的标志,无符号数运算记录
在这里插入图片描述
在这里插入图片描述

OF 标志

Overflow Flag,溢出标志位,有符号数运算记录
在这里插入图片描述

DF 标志

Direction Flag,方向标志位。在串处理指令中,控制每次操作后 si、di 的增减。
在这里插入图片描述
由 cld 和 std 来置 0 或 置 1

其他指令记录

inc 和 dec

inc bx的含义是bx中的内容加1,inc 是 increase 的意思

dec 则是减1,dec 是 decrement 的意思

and 和 or

and:逻辑与
or:逻辑或
在这里插入图片描述
一个技巧(小写变大写):
因为小写字母的ASCII码比大写字母的ASCII码值大 32 即 20H,所以可以这样:and al, 11011111B,即将al 中的 ASCII 码的第 5 位置置为0,变成大写字母。

[bx+idata]

以下四种等价:

mov ax,[bx+200]
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200

X ptr

在这里插入图片描述
在这里插入图片描述

div

在这里插入图片描述
在这里插入图片描述

mul

在这里插入图片描述

db,dw 和 dd

汇编伪指令
db:define byte,定义字节型数据
dw:define word,定义字型数据
dd:define double word,定义双字型数据

比如 db 1,2,3,4,5,6,就定义出这六个字节的值为1,2,3,4,5,6,例子:
在这里插入图片描述
同理有 dw 和 dd

dup

在这里插入图片描述
在这里插入图片描述

转移指令

在这里插入图片描述

offset

在这里插入图片描述
上图,offset 操作符取得了标号 start 和 s 的偏移的地址 0 和 3 .(第一条指令长度为 3 字节,所以第二个 offset 为3)

jmp

jmp 为无条件转移指令,可以只修改 IP (段内转移),也可以同时修改 CS 和 IP(段间转移)。
在这里插入图片描述

段内短转移jmp short 标号 和 段内近转移 jmp near ptr 标号

根据位移进行转移的jmp指令:
在这里插入图片描述
上图的 jmp short s 编译出来的机器码为 EB03 ,03 表示偏移量,因为下一条指令 add ax,1 三个字节,偏移三个字节到达目的位置 s 。
在这里插入图片描述

段间转移 jmp far ptr 标号

转移的目的地址在指令中的jmp指令:

前面的两个段内转移指令,其对应的机器指令中并没有转移的目的地址,而是相对于当前 IP 的转移位移。

这里的jmp far ptr 标号实现的是段间转移,又称为远转移。功能如下:
在这里插入图片描述

jmp 寄存器

在这里插入图片描述

jmp word ptr 内存单元地址jmp dword ptr 内存单元地址

转移地址在内存中的 jmp 指令:

jmp word ptr 内存单元地址 属于 段内转移,只改了 IP:
在这里插入图片描述

jmp dword ptr 内存单元地址 属于 段间转移,改了 CS 和 IP:
在这里插入图片描述

jcxz

在这里插入图片描述

loop

在这里插入图片描述

call 和 ret

call 和 ret 指令都是转移指令,它们都修改 IP ,或同时修改 CS 和 IP。它们经常被共同来实现子程序的设计。

ret 和 retf

在这里插入图片描述

call

在这里插入图片描述

call 标号
在这里插入图片描述
call far ptr 标号
在这里插入图片描述
call 寄存器
在这里插入图片描述
call word ptr 内存单元地址call dword ptr 内存单元地址
在这里插入图片描述
在这里插入图片描述

call 和 ret 的配合使用

在这里插入图片描述
在这里插入图片描述
同样,可能涉及到寄存器冲突的问题,于是我们可以用栈来保存寄存器的内容,一个标准框架为:
在这里插入图片描述

nop

NOP是英语“No Operation”的缩写。NOP无操作数,所以称为“空操作”。

CPU 遇到 nop 指令,什么都不做,占用一个字节。执行NOP指令只使程序计数器PC加1,所以占用一个机器周期。

实例:MOVLW 0xOF ;送OFH到W MOVWF PORT_B ;W内容写入B口 NOP ;空操作 MOVF PORT_B,W ;
读操作说明:该三条指令是一种对I/O口的B口连续操作的实例,其目的达到写入B口的内容要读出时,应保证写、读之间有个稳定时间,因此加入了空操作指令NOP。

来自https://www.cnblogs.com/shangzhijian/p/4994028.html

adc 指令

在这里插入图片描述

检测标志位的条件转移指令

所有条件转移指令的转移位移都是 [-128, 127]
在这里插入图片描述
在这里插入图片描述

例子:

mov ax,2
mov bx,1
sub bx,ax
adc ax,1

由于 sub 记录的 1-2 ,发生了借位,所以CF=1,因此最终的计算:(ax)+1+CF=2+1+1=4.

adc中的CF值和它前面的指令相关,有了adc指令就能方便地进行加法的第二步运算,比如下面的指令和add ax,bx等价:

add al,bl
adc ah,bh

sbb 指令

在这里插入图片描述

cmp 指令

在这里插入图片描述
我们希望得到逻辑上真正结果的正负。对无符号数进行比较之后,可以考察 zf(是否相等) 和 cf (是否借位)寄存器;对有符号数进行比较,我们应该考察 sf(得到实际结果的正负)和 of(得知有没有溢出)。

串传送指令

movsb

把 ds:si 搬到 es:di 里头,然后再对 si 和 di 操作:
在这里插入图片描述
在这里插入图片描述

movsw

在这里插入图片描述

rep

rep 是 repeat 的意思,意味着循环:
在这里插入图片描述
在这里插入图片描述

cld 和 std

由于 flag 的 df 位决定着串传送指令执行后,si 和 di 改变的方向,所以CPU设置了 cld 和 std 指令:

cld 指令:将标志寄存器的 df 位 置0;
std 指令:将标志寄存器的 df 位 置1;

sti 和 cli

8086CPU 提供的设置标志寄存器的 IF 位的指令:

sti:设置 IF=1;
cli:设置 IF=0;

pushf 和 popf

pushf 和 popf 为直接访问标志寄存器提供了一种方法。

pushf:将标志寄存器的值压栈。
popf:从栈中弹出数据,送入标志寄存器。

int 和 iret

int 是 interrupt 的意思,格式为 int n,n 为中断类型码,它的功能是引发中断过程。

可以在程序中使用 int 指令调用任何一个中断的处理程序。可见其与 call 指令类似,都是调用一段程序。
在这里插入图片描述
call 与 ret 配对,而 int 则于 iret 配对:

int 的过程:
在这里插入图片描述
iret 的过程:
在这里插入图片描述
即执行完中断例程后,用 iret 指令恢复 int 指令执行前的标志寄存器和 CS、IP 的值,从而接着执行程序。

in 和 out

访问端口,见下面 端口 一节。

shl 和 shr

逻辑移位指令。如果移动位数大于 1 ,必须将移动位数放在 cl 中。

shl:
在这里插入图片描述
在这里插入图片描述

shr:
在这里插入图片描述
在这里插入图片描述

指令系统总结

在这里插入图片描述
在这里插入图片描述

组织数据

代码段与多个段的程序

数据、代码、栈放入一个段中

问题:混乱; 数据、栈和代码需要的空间超过 64 KB就不能放在一个段中(对于8086CPU,一个段的容量不能大于 64 KB)。

一个安排程序的框架:
在这里插入图片描述
示例:
在这里插入图片描述
dw的含义是定义字型数据,dw即“define word”,上图第一个字存在 cs:0 开始的地址(猜测是因为第一行的assume cs:codesg,把代码段的初始地址一开始保存在cs中,但是书本第132页说,assume是伪指令,是编译器执行的,也是仅在源程序中存在的信息,CPU并不知道它们),逐次偏移+2.

end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方(上图为start)。

数据、代码、栈放入不同的段

示例:
在这里插入图片描述
这样划分出三个段就清晰很多了,上图是我写的一段伪代码。

程序从哪开始由 start 指示。当然这里code、data、stack、start都能改成别的字母,只是一个标记而已。

我们想把数据段中的某个数据放入寄存器,则可以这样写:

mov ax, data
mov ds, ax
mov bx, ds:[6]

(8086CPU不允许一个数值直接送入段寄存器中,这里的data会被编译器处理为一个表示段地址的数值,因此这里还需要用ax寄存器加以转化)

数据标号

详见第 16 章。

在以往写为:

assume cs:code

code segment
	a: db 1, 2, 3, 4, 5, 6, 7, 8
	b: dw 0

使用数据标号,写为:

assume cs:code

code segment
	a db 1, 2, 3, 4, 5, 6, 7, 8
	b dw 0

去掉 “:” ,同时描述内存地址和单元长度。(有冒号则是仅表示地址的地址标号)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这一块建议仔细阅读 P289 16.2 ,此处不再记录。

中断

任何一个通用的 CPU 比如 8086,都具备这种能力:在执行完当前正在执行的指令之后,检测到从 CPU 外部发送过来的或内部产生的一种特殊信息,并且可以立刻对所接收到的信息进行处理。

这种特殊的信息就被称为 中断信息。中断的意思是,CPU 不再接着向下执行,而是转去处理中断信息。

中断信息可以来自于 CPU 的内部和外部。

内部中断

发生这四种情况,产生相应的中断信息:
在这里插入图片描述

中断类型码

中断信息必须包含识别来源的编码。8086 CPU 用称为中断类型码的数据来标识中断信息的来源。

中断类型码为一个字节型数据,可表示256种中断信息的来源。它的作用就是用来定位中断处理程序。

上面 4 种中断源,在 8086 CPU 种的中断类型码如下:
在这里插入图片描述
在这里插入图片描述

中断向量表 与 中断处理程序

中断向量表在内存中存放,对于 8086PC 机,存于内存地址0处(0000:0000 ~ 0000:03FF)。

中断向量:中断处理程序的入口地址(8086CPU中,这个入口地址包括段地址和偏移地址,所以一个表项两字,高地址字存放段地址,低地址字存放偏移地址);

中断向量表:中断向量的列表(即中断处理程序入口地址的列表);

中断处理程序:用来处理中断信息的程序(通过中断类型码来从中断向量表中查找,从而定位中断处理程序,中断处理程序由程序员编写)

在这里插入图片描述
找到这个入口地址的最终目的就是设置 CS 和 IP,使 CPU 执行中断处理程序。

中断过程

用中断类型码找到中断向量,并用它设置 CS 和 IP,这个工作是 CPU 的硬件自动完成的。CPU 硬件完成这个工作的过程被称为中断过程。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

中断处理程序 和 iret 指令

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

单步中断

在这里插入图片描述
这里标志寄存器入栈之后,可以看到 TF 位被置为0,否则 TF 位还是 1 那执行完单步中断的处理程序之后就又会中断,这不死循环套娃了嘛…

因此可以看到,所谓Debug去调试没啥特殊的,就是利用了单步中断的功能。比如执行某指令(按下某按键)让 TF 置1,从而进入我们自己写的单步中断处理程序。

因此我们也可以看到,CPU 提供单步中断功能的原因就是,为单步跟踪程序的执行过程提供了实现机制。

响应中断的特殊情况

有的特殊情况下,执行完当前指令后即使发生中断也不会响应。

最典型的比如 对 ss 寄存器操作,向 ss 寄存器传入数据,ss:sp 指向栈顶,所以对 ss 和 sp 的设置应该连续完成:
在这里插入图片描述
在这里插入图片描述

BIOS 和 DOS 所提供的中断例程

中断处理程序 简称为 中断例程(P253)。
在这里插入图片描述
BIOS 和 DOS 所提供的中断例程 安装在例程中的安装过程:
在这里插入图片描述

外中断

外设的输入是送入相关的接口芯片的端口中(不是直接送入内存和CPU);CPU向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。

CPU还可以向外设输出控制命令,这些命令也是先送到相关芯片的端口中,再由相关芯片根据命令对外设实施控制。

即CPU通过端口和外部设备进行联系。比较典型的外设比如键盘和显示器。

外中断信息

在这里插入图片描述
PC 系统中,外中断源分为两类:
在这里插入图片描述
在这里插入图片描述

实际例子:键盘输入

键盘上每个键都相当于一个开关,键盘中有一个芯片对键盘上每一个按键的开关状态进行扫描。

按下 松开 按键都会产生扫描码。按下产生的扫描码被称为通码,松开产生的则为断码。

大致分为三步:

  1. 键盘产生扫描码,并被送入 60h 端口中。
  2. 键盘的输入到达 60h 端口中,相关的芯片就会向 CPU 发出中断类型码为 9 的可屏蔽中断信息。
  3. CPU 检测到该中断信息后,如果 IF=1,则响应中断,引发中断过程,转去执行 int 9 的中断例程。(BIOS 提供了 int 9 中断例程,用来进行基本的键盘输入处理)

这些步骤中,我们能改变的只有 int 9 的中断例程。其余都是硬件系统完成的。

端口

在这里插入图片描述
如上图,端口即图中所述三种芯片所提供的接口,用于访问芯片内部的寄存器。

访问端口时,CPU 通过端口地址来定位端口,端口地址通过地址总线来传送。8086CPU 16根访问端口的地址总线,于是最多定位 64 KB 个不同的端口。即端口地址的范围是 0 ~ 65535.

端口的读写指令只有两条:in 和 out,分别用于从端口读取数据和往端口写入数据。

(1)访问内存:
在这里插入图片描述
(2)访问端口
在这里插入图片描述

在这里插入图片描述

补充

  1. 在汇编源程序中,数据不能以字母开头,所以要在前面加0. 比如 “A000h” 要写为 “0A000h”。(P104)
  2. debug中可以用p命令来将循环一次执行完,或者用g命令来直接执行到某处。
  3. 在汇编程序中,我们可以用 ‘…’ 的方式指明数据是以字符的形式给出的,编译器将把它们转化为相对应的 ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)码(小写字母的ASCII码比大写字母的ASCII码值大 32 即 20H)。
    比如 db 'unIX' 相当于 db 75H,6EH,49H,58H,后者分别是字母unIX的ASCII码。
  4. 汇编语言中用3个概念来表达数据的位置:
    i. 对于直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中),在汇编语言中称为:立即数(idata),在汇编指令中直接给出。
    ii. 对于指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。
    iii. 对于指令要处理的数据在内存中,在汇编指令中可用 [X] 的格式给出 EA(偏移地址),SA(段地址)在某个寄存器中。存放段地址的寄存器可以是默认的(ds或者ss)也可以是显式地给出。
  5. “-”是编译器识别的运算符号,编译器可以用它来进行两个常数的减法。(P245)比如mov ax 8-4
  6. bx (以及无寄存器的情况) 的默认段地址为 ds,bp 则为 ss,若 [] 中有寄存器,则必有 bx 或者 bp (作为基址)(见笔记 bx、si、di 和 bp 一节)
  7. CMOS RAM 中存储的时间信息:
    在这里插入图片描述
  8. 数值+30h = 对应字符的 ASCII 值 (0~9 和 “0” ~ “9”)
    数值+37h = 对应字符的 ASCII 值(10~15 和 “A” ~ “F”)
    可以做一个直接定址表,表中依次存储字符 “0” ~ “F”,我们可以通过数值 0~15 直接查找到对应的字符。

附注(Intel 系列微处理器的 3 种工作模式):

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-12-15 18:27:31  更:2021-12-15 18:28:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 15:27:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码