1.学习汇编语言的理由
汇编语言仍在发挥不可替代的作用
- 效率
- 运行效率:开发软件的核心部件,快速执行和实时响应
- 开发效率:做合适的事,开发效率无敌
- 底层:计算机及外围设备的驱动程序
- 操作系统的内核
- 嵌入式系统:家用电器、仪器仪表、物联网。。
-汇编语言在学习计算机中起到独特作用 - 加深对计算机组成原理和操作系统等的独到理解
- 向上理解各种软件系统的原理
- 向下掌握硬件系统的原理
3由机器语言到汇编语言
机器语言 是机器指令的集合
机器指令 是一台机器可以正确执行的命令
机器指令 是一台二进制数。使用电平脉冲来表示0和1。
但是机器语言:不易纠错。
汇编语言
汇编语言的主体是汇编指令
汇编指令和机器指令的差别在于指令的表示方法上
汇编指令是机器指令便于记忆的书写格式。
汇编指令是机器指令的助记符。
寄存器:CPU中可以存储数据的器件,一个CPU中有多个寄存器
汇编语言程序:伪指令,汇编指令,其他符号、
汇编阶段:汇编器将汇编代码转变成机器可以执行的命令,每一个汇编语句几乎都对应一条机器指令。
4.计算机的组成
主板上有
- CPU
- 总线
- 内存(内存条)
- 扩展槽(接外部设备)
CPU由三条总线控制内存
地址总线,数据总线,控制总线。
计算机的组成

CPU是计算机的核心部件,控制整个计算机的运作并进行运算,要想让一个CPU工作,必须向它提供指令和数据。指令和数据是存放在内存中的。
指令和数据的表示:
数据和指令都是二进制信息。
数据表示方法:
B(二进制)
H(十六进制)
O(八进制)
D(十进制)
计算机中的存储单元
存储器被划分为若干个存储单元,每个存储单元从0开始编号。
8086有20条数据线,寻址空间为2^20,1MB
电子计算机的最小信息单位是bit,也就是一个二进制位,8个bit组成一个byte,也就是一个字节。
计算机中总线
在计算机中有专门连接CPU和其他芯片的导线,这个称为总线。
物理上:一根根导线的集合
逻辑上:地址总线,数据总线,控制总线。

CPU想要进行数据的读写,必须和外部期间进行信息交互
1.存储单元的地址(地址信息)
2.器件的选择,读或写的命令(控制信息)
3.读或写的数据(数据信息)
地址总线
CPU是通过地址总线来指定存储单元的。
地址总线:CPU是通过地址总线来指定存储单元的。
地址总线宽度,决定了可寻址的存储单元大小
N根地址总线(宽度为N),对应寻址空间2^N
数据总线
CPU与内存或者其他器件之间的数据传送是通过数据总线来进行的。
数据总线的宽度决定了CPU和外界的数据传送的速度。
控制总线
CPU通过控制总线控制外部器件
控制总线是不同控制线的集合
控制总线宽度决定了CPU对外部器件的控制能力

32位地址总线的CPU,寻址能力为4GB。
5.内存的读写与地址空间
CPU对存储器的读写
CPU想要进行数据的读写,必须和外部器件进行三类信息的交互:
存储单元的地址(地址信息)
器件的选择,读或写的命令(控制信息)
读或写的指令(数据信息)
内存地址空间

RAM:随机存储器,能读能写。临时存放数据用的,关机后啥也没有了
ROM:只读存储器,只能读不能写,存放有固定数据,且删不掉也无法覆盖其他数据
将各类存储器看作一个逻辑存储器-统一编址
所有的物理存储器被看作一个由若干存储单元组成的逻辑存储器。
每个物体存储器在这个逻辑存储器中占有一个地址段,即一段地址空间。
CPU在这段(逻辑)地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据,


7.访问寄存器和内存


8.寄存器及数据存储
寄存器是CPU内部的信息存储单元
8086CPU中有14个寄存器
通用寄存器:AX,BX,CX,DX
变址寄存器:SI,DI
指针寄存器:SP,BP
指令指针寄存器:IP
段寄存器:CS,SS,DS,ES
标志寄存器:PSW
8086CPU中所有寄存器都是16位的,可以存放两个字节。
AXaccumulator 累加寄存器 通常用来执行加法,函数调用的返回值一般也放在这里面
BX base 基址寄存器 读写I/O端口时,edx用来存放端口号
CX counter 计数寄存器 通常用来作为计数器,比如for循环
DX data 数据寄存器 数据存取
SI source index 源变址寄存器 字符串操作时,用于存放数据源的地址
DI destination index 目标变址寄存器 字符串操作时,用于存放目的地址的,和esi两个经常搭配一起使用,执行字符串的复制等操作
SP stack pointer 栈指针寄存器 栈顶指针,指向栈的顶部
BP base pointer 基址指针寄存器 栈底指针,指向栈的底部,通常用ebp+偏移量的形式来定位函数存放在栈中的局部变量
eip: 指令寄存器可以说是CPU中最最重要的寄存器了,它指向了下一条要执行的指令所存放的地址,CPU的工作其实就是不断取出它指向的指令,然后执行这条指令,同时指令寄存器继续指向下面一条指令,如此不断重复
ES extra segment 附加段寄存器
CS code segment 代码段寄存器
SS stack segment 栈段寄存器
DS data segment 数据段寄存器
横看成岭侧成峰
问题:8086上一代CPU中的寄存器都是8位的,如何保证程序的兼容性 。
方案:通用寄存器均可以分为两个独立的8位寄存器使用。
细化:
AX:AH,AL
BA:BH,BL
CX:CH,CL
DX:DH,DL

字在存储器中的存储
- 8086是16位CPU,字长为16bit
- 一个字可以存储在一个16位寄存器中,
- 这个字的高位字节存储在这个寄存器的高8位寄存器
- 这个字的低位字节存储在这个寄存器的低8位寄存器
- 一个寄存器,大小为16位,可以存放两个字节,可以存放一个字。
(上面这种存储方式就是小端存储)
这里。。。看看如何区分大端or小端
小端:低位字节存放在内存的低地址端,高位字节存放在内存的高地址端(CPU对操作数的存放方式是从低字节到高字节)
大端:高位字节存放在内存的低地址端,低位字节存放在内存的高地址端(CPU对操作数的存放方式是从高字节到低字节)

#include<stdio.h>
int main()
{
int a = 0x44332211;
char* b = (char*)&a;
(*b == 0x11) ? printf("Little-endian\n") : printf("Big-endian\n");
return 0;
}
9.mov和add指令


最后一个,低8位会溢出,不可进位。
10.确定物理地址的方法
物理地址
-
CPU访问内存单元时要给出内存单元的地址。
-
所有的内存单元构成的存储空间是一个一维的线性空间。
-
每一个内存单元在这个空间中都有唯一的地址,这个唯一的地址就是
物理地址。 -
8086有20位地址总线,可以传送20位地址,寻址能力为1M,
-
8086是16位结构的CPU(数据总线为16位),运算器一次最多可以处理16位的数据,寄存器的最大宽度为16位。
-
在8086内部处理的、传输、暂存的地址也是16位,寻址能力也只有64KB。
如果处理在寻址空间上的这个矛盾,8086CPU的解决方法: -
用两个16位地址(段地址,偏移地址)合成一个20位的物理地址。
-
地址加法器合成物理地址的方法:物理地址=段地址*16+偏移地址。

物理地址=段地址*16+偏移地址
解决的问题:
- 用两个16位的地址(段地址,偏移地址)相加得到一个20位的物理地址。
- CPU在访问内存时,用一个基础地址(段地址*16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。
11.内存的分段表示法
用分段的方式管理内存
8086CPU中使用(段地址*16)+偏移地址=物理地址 的方式给出内存单元的物理地址。
内存并没有分段,段的划分来自于CPU。
同一段内存,具有多种多段方案
1)段地址*16,必然是16的倍数,因此,一个段的起始地址也一定是16的倍数
2)偏移地址为16位,16位地址的寻址能力为64K,所以一个段的最大长度为64K
存储单元地址的表示方法:
例:数据在21F60内存单元中,段地址是2000H,
a)数据存在内存2000:1F60单元中
b)数据存在内存的2000H段中的1F60H单元中
物理地址=段地址*16+偏移地址
段地址–用来存放段地址。
4个段寄存器
CS-代码段寄存器
DS-数据段寄存器
SS-栈段寄存器
ES-附加段寄存器
13.CS、IP与代码段
CS:代码段寄存器
IP:指令指针寄存器
CS:IP:CPU将内存中CS:IP指向的内容当作指令执行
8086PC工作过程的简要描述
1)从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器
2)IP=IP+所读取指令的长度,从而指向下一条指令
3)执行指令,转到步骤1)重复这个过程。
接上最开始的问题,计算机如何知道一个二进制信息是数据还是指令?
CPU将CS:IP指向的内存单元中的内容看作指令。
14.jmp指令(跳转指令)
执行何处的指令,取决于CS:IP,因此可以通过改变CS:IP中的内容,来控制CPU要执行的目标指令。
如何改变CS:IP中的值呢?
1)DEBUG中的R命令可以改变寄存器的值–RCS RIP。(只是调试手段)
2)用指令修改(8086CPU不提供对IP的修改的指令)
3)转移指令jmp
转移指令jmp
1)同时修改CS,IP的内容 (jmp 段地址:偏移地址)
用jmp中给出的段地址修改CS,jmp中给出的偏移地址修改IP
2)仅修改IP的内容 (jmp 某一合法寄存器)jmp ax。
用寄存器中的值修改IP
15内存中字的存储
8086CPU中,16位作为一个字
1)16位的字存储在一个16位的寄存器中,如何存储
高8位放高字节,低8位放低字节
2)16位的字在内存中需要两个连续字节存储,怎么存放
低位字节存放在低地址单元,高位字节存放在高地址单元
字单元:由两个地址连续的内存单元组成,存放一个字型数据(16位)
16用DS和[address]实现字的传送
CPU从内存单元中要读取数据
1)CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址。
2)8086CPU中,内存地址(物理地址)= 段地址:偏移地址。
3)可以使用,DS 和 [address]配合。
DS寄存器:要访问数据的段地址
[…]:要访问数据的偏移地址。
1)必须使用通用寄存器给段寄存器赋值

例
mov al, [0]
段地址:DS中
偏移地址:0
al为ax的低位字节,只读取一个字节,将该处的字节型数据读到al中
mov ax, [0]
将该处的字型数据读到ax中(16位)
17DS与数据段
8086CPU中(这个条件是前提!!!,整个课程的前提)
数据段:将一组长度为N(N<64k)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间
DS:[address]
DS:存放数据段的段地址
[address]:偏移地址
用 DS:[address] 形式访问内存中数据段方法
。。
18栈及栈操作的实现
栈的操作规则:先进后出。
CPU中提供了栈机制:
push ax:将ax中的数据传入栈中。
pop ax:从栈顶取出数据送入ax,(以字为单位对栈进行操作)
8086CPU中,有两个与栈相关的寄存器
栈段寄存器SS:存放栈顶的段地址
栈顶指针寄存器SP:存放栈顶的偏移地址。
任意时刻,SS:SP指向栈顶元素。
push和pop指令执行过程
栈是从高地址向低地址扩展。
push ax:
- SP=SP-2;
2)将ax中的内容送入SS:SP指向的内存单元,SS:SP此时指向新栈顶。
pop ax:
1)将 SS:SP指向的内存单元处的数据送入ax中,
2)SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

19关于“段”的总结
物理地址=段地址*16+偏移地址
三种段:
数据段:
- 将段地址放在DS中。
代码段:
- 将段地址存放在CS中,将段中第一条指令的偏移地址放在IP中。
栈段:
- 将段地址放在SS中,将栈顶单元的偏移地址存在SP中,
- CPU在需要进行栈操作(push,pop)时,就可以将我们定义的栈段当做栈空间来使用。
21用汇编语言写的源程序
汇编程序:包括汇编指令和伪指令

伪指令:没有对应的机器码的指令,最终不被CPU所执行。
伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。

assume(假设):
假设某一段寄存器和程序中的某一个用 segment … ends 定义的段相关联
assume cs:codesg 指 CS寄存器与codesg关联,将定义的codesg当作程序的代码段使用。
段定义:
一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或当作栈空间来使用。
一个有意义的汇编程序中至少要有一个段,这个段用来存放代码。
定义程序中的段:每个段都需要有段名
段名 segment ——段的开始
…
段名 ends ——段的结束
end (不是ends):
汇编程序的结束标记。若程序结尾处不加end,编译器在编译程序时,无法知道程序在何处结束。
22由源程序到程序执行
源程序 到 可执行文件 的过程
源程序文件.asm (编译)-- 目标文件.obj (连接)-- 可执行文件.exe
编译

目标文件(.OBJ):对一个源程序进行编译要 得到的最终结果。
列表文件(.LST):编译器将源程序编译为目标文件的过程中产生的中间结果。
交叉引用文件(*.CRF):同 列表文件 一样,是编译器将 源程序编译为目标文件 过程中产生的中间结果。
对源程序的编译结束,编译器输出的最后两行告诉 我们这个源程序没有警告 错误和必须要改正的错误。
连接

可执行文件(.EXE):对一个程序进行连接要得到的最终结果。
映像文件(.MAP):连接程序将目标文件连接为可执行文件过程中产生的中间结果。
库文件(.LIB):包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件中的子程序,就需要在连接的时候,将这个库文件和我们的目标文件连接到一起,生成可执行文件。
no stack segment,一个“没有栈段”的警告错误 ,可以不理会这个错误。
23运行及跟踪
–debug的运行跟踪
继续命令P(Proceed): 类似T命令,逐条执行 指令、显示结果。但遇子程序、中断等时,直接执行,然后显示结果。
运行命令G(Go):从指定地址处开始运行程序, 直到遇到断点或者程序正常结束.

24[…]和(…)
[…]——(汇编语法规定)表示一个内存单元

(…)——(为学习方便做出的约定)表示一个内存单元或寄存器中的内容


25Loop指令
定义一个标号
loop 标号
cx中提前存放循环次数
CPU 执行loop指令时要进行的操作
①(cx)=(cx)-1;
②判断cx中的值
不为零则转至标号处执行程序
如果为零则向下执行

loop 标号
实际上改变的是ip地址。

26Loop指令使用再例
:问题:计算ffff:0006 字节单元中的数乘以3,结果存储在dx中

27段前缀的使用
Debug中,mov al, [0]的功能是
——将DS:0存储单元的值传给AL
但是在写好程序编译并连接后,编译好的程序中,
mov al, [0] 变成了将常量0传给AL
这些出现在访问内存单元的指令中,用于显式地指明内存单元的段地址的“ds:”、“cs:”、“ss:”或“es:”,在汇编语言中称为段前缀。
计算ffff:0~ffff:b字节单元中的数据的和,结果存储在dx中
(1)运算后的结果是否会超出 dx 所能存储的范围?
ffff:0~ffff:b内存单元中的数据是字节型数据,范围在0~255之间,12个
这样的数据相加,结果不会大于 65535,可以在dx中存放下。
(2)是否可以将 ffff:0~ffff:b中的数据直接累加到dx中?
add dx, ds:[addr] ;(dx)=(dx)+?
实际:取出的是内存中的16位数据
(3)是否可以将 ffff:0~ffff:b中的数据直接累加到dl中?
add dl, ds:[addr] ;(dl)=(dl)+?
实际:取出的是内存中的8位数据,但很有可能造成进位丢失
(4)对策:取出8位数据,加到16位的寄存器
mov al, ds:[addr]
mov ah, 0
add dx, ax
28在代码段中使用数据
应用案例

29在代码段中使用栈
问题:完成下面的程序,利用栈,将程序中定义的数据逆序存放。
程序的思路大致如下:
1)程序运行时,定义的数据存放在cs:0~cs:F单元中,共8个字单元。
2)依次将这8个字单元中的数据入栈,然后再依次出栈到这 8 个字
单元中,从而实现数据的逆序存放。
3)栈需要的内存空间,在程序中通过定义“空”数据来取得

push ax:
- SP=SP-2;
2)将ax中的内容送入SS:SP指向的内存单元,SS:SP此时指向新栈顶。
pop ax:
1)将 SS:SP指向的内存单元处的数据送入ax中,
2)SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
30将数据,代码,栈放入不同段

SS:SP:
栈段寄存器SS:存放栈顶的段地址
栈顶指针寄存器SP:存放栈顶的偏移地址。
DS:数据段 段地址
CS:自动赋值,不用指定
3933

被折叠的 条评论
为什么被折叠?



