type
status
date
slug
summary
tags
category
icon
password
以下学习内容 from 王爽汇编语言(第三版)
一,计算机组成
1.CPU
CPU是计算机的核心部件,它控制整个计算机的运作并进行运算。想要让一个CPU工作,就必须向它提供指令和数据(from 存储器)
2.存储器(内存)
指令和数据在存储器(内存)中存放。离开了内存,CPU无法工作
3.指令和数据的表示
问:二进制信息1000100111011000是数据还是指令?
其实这个要看CPU,如果看作89D8H,则是数据,如果看作MOV AX,BX,则是指令
4.计算机中的存储单元
例如:一个存储器有128个存储单元,编号0~127
5.计算机中的总线
在计算机中有专门有链接CPU和其他芯片的导线,通常称为总线(一根根导线的集合)
- 三类总线:
- 地址总线 CPU是通过地址总线来指定存储单元的; 地址总线的宽度,决定了可寻址的存储单元大小; N根地址总线(宽度为N),对应寻址空间为2**N
- 数据总线 CPU与内存或其它器件之间的数据传送时通过数据总线来进行的; 数据总线的宽度决定了CPU和外界的数据传送速度; 例如:向内存中写入数据89D8H时,8位数据总线要传送两次(第一次00011011 D8 ;第二次10010001 89);而16位数据总线只用传送一次(1001000100011011 89D8)
- 控制总线 CPU通过控制总线对外部期间进行控制; 控制总线时一些不同控制线的集合; 控制总线的宽度决定了CPU对外部器件的控制能力
CPU | 地址总线宽度 | 寻址能力 | 数据总线宽度 | 一次传送数据 | 读取1KB数据要读X次 |
8086 | 20 | 1MB(2**20) | 16 | 2B | 512 |
80286 | 24 | 16MB(2**24) | 16 | 2B | 512 |
80386 | 32 | 4GB(2**32) | 32 | 4B | 256 |
6.CPU对存储器的读写
三类信息交互:
1. 存储单元的地址(地址信息)
2. 器件的选择,读或写的命令(控制信息)
3. 读或写的数据(数据信息)
- 演示
机器码:1010000000000001100000000
16进制:A0030
汇编指令:MOV AL,[3]
含义:从3号单元读取数据送入寄存器AL
- 过程:
CPU先通过地址线将要读取的地址信息[3]送入内存,锁定3号单元;
再通过控制线将控制信息‘读’传送到内存;
最后将3号单元内的数据08通过数据线传给CPU
7.内存地址空间
- 什么时内存地址空间?(以8086为例) 8086CPU地址总线宽度为20,可以寻址1MB个内粗你单元,那么内存地址空间为1MB
二.访问寄存器和内存
1.寄存器及数据存储(指令)
8086CPU有14个寄存器:
通用:AX、BX、CX、DX
变址:SI、DI
指针:SP、BP
指令指针:IP
段:CS、SS、DS、ES
标志:PSW
共性:都是16位,可以存放两个字节,最大值:2**16-1
mov和add、sub指令
汇编指令 | CPU完成的操作 | 描述 |
mov ax,18 | 将18送入AX | AX = 18 |
mov ax,bx | 将寄存器BX里的数据送入寄存器AX | AX = BX |
add ax,8 | 将寄存器AX中的数据加8 | AX += 8 |
add ax,bx | 将AX,BX中的内容相加,存在AX中 | AX = AX + BX |
sub ax,18 | 将寄存器AX中的数据减18 | AX -= 18 |
sub ax,bx | 将寄存器AX的值减去BX的值 | AX = AX - BX |
特殊:通用寄存器可以拆为两个8位寄存器表示,比如AX,可以拆为AH(高位)和AL(低位)
mov操作是‘送入’,没有加的意思。比如一开始AX寄存器中的数据为2640H,经过汇编指令mov AH,0后,变为0040H
jmp指令
8086CPU大部分寄存器的值,都可以用mov指令(传送指令)来改变
但是,mov指令不能用于设置CS、IP的值
能改变的指令是jmp指令(转移指令)
- 同时修改CS、IP的内容 jmp 段地址:偏移地址
- 仅修改IP的内容 jmp 某一合法寄存器
分析一个问题:
- 从20000H开始,执行的序列是:
(1) mov ax,6622
(2) jmp 1000:3
(3) mov ax,0000
(4) mov bx,ax
(5) jmp bx
(6) mov ax,0123H
(7) 跳转至第(3)步执行(至此循环)
inc指令
加一的意思
Loop指令
功能:实现循环(计数型循环)
执行时要执行的操作:
- CX = CX -1 (因此CX寄存器又被称为计数寄存器)
- 判断CX中的值,不为0则继续跳转;为0则向下执行
这段汇编代码作用实现2**12
2.确定物理地址的方法
8086CPU有20位地址总线,可以传送20位地址,寻址能力1M
但8086是16位结构的CPU,运算器一次最多可以处理16位数据,寄存器最大宽度也是16位,所以内部处理、传输、暂存的地址也是16位,寻址能力只有64KB
8086如何处理寻址空间上的矛盾?
用两个16位地址(段地址、偏移地址)合成一个20位物理地址
地址加法器合成物理地址的方法:物理地址 = 段地址 x 16 + 偏移地址
段地址 x 16其实是十六进制段地址数据左移一位
- 同一段内存,可以有多种分段方案
起始地址(基础地址)为10000H,段地址为1000H,大小为100H
起始地址为10000H和100080H,段地址为1000H和1008H,大小均为80H
- PS: 段地址 x 16必然是16的倍数,所以一个段起始地址一定是16的倍数 偏移地址为16位,寻址能力64k,所以一个段最大长度64k
段地址很重要,所以专门寄存器存放段地址
CS—代码段寄存器
DS—数据段寄存器
SS—栈段寄存器
ES—附加段寄存器
3.段总结
段基础
物理地址 = 段地址 x 16 + 偏移地址
做法
可以根据需要将一组内存单元定义为一个段;
将起始地址为16的倍数,长度为N(N≤64K)的一组地址连续的内存单元定义为一个段;
将一段内存定义为一个段后,用一个段地址指示段,用偏移地址访问段内的单元(自由安排)
4.CS和IP寄存器
CS—代码段寄存器,IP—指令指针寄存器(表示方法:CS:IP)
IP寄存器存的是偏移地址
5.内存中字的存储
对8086CPU,16位作为一个字。存储在一个16位的寄存器中,高8位放高字节,低8位放低字节
6.CPU从内存单元中读取数据
读取一个内存单元时,必须先给出这个内存单元的地址
原理:在8086PC中,内存地址由段地址和偏移地址组成(段:偏移)
解决方案:DS寄存器和[address]配合
DS寄存器存放要访问的数据的段地址
偏移地址用[……]形式直接给出
特殊:段地址送入DS:数据→一般的寄存器→段寄存器(重要)
字的传送
8086CPU可以一次性传送一个字(16位数据)
DS与数据段
- 字在内存中存储时,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放在高地址单元中;
- 用mov指令要访问内存单元,可以在mov指令中只给出单元的偏移地址,此时,段地址默认在DS寄存器中;
- [address]表示一个偏移地址为address的内存单元
7.栈结构
- 栈时一种只能在一段进行插入或删除操作的数据结构
两个基本操作:入栈和出栈
入栈(PUSH):将一个新的元素放到栈顶
出栈(POP):从栈顶取出一个元素
push ax:将ax中的数据送入栈中
pop ax:从栈顶取出数据送入ax
以上操作以字为单位对栈进行操作
栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。操作规则:LIFO(Last In First Out,后进先出)
CPU提供的栈机制:
提供栈相关指令,支持用栈的方式访问内存空间;可以将一段内存当作栈来使用
试例:设将10000H~1000FH内存当作栈来使用
根据图内指令序列,先将0123H存入ax寄存器,然后将ax内的数据入栈,此时1000FH中的数据为01,1000EH中的数据为23;
同理操作,最后pop出来时从栈顶(在这个例子中是1000AH)出栈依次存入ax,bx,cx
CPU如何知道一段内存空间被当作栈使用?
需要有两个与栈相关的寄存器:
栈顶寄存器SS:存放栈顶的段地址
栈顶指针寄存器SP:存放栈顶的偏移地址
栈空时,SS:SP指向栈顶10010H
push指令和pop指令的执行过程
push、pop指令实质上是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的
- push ax (1),SP = SP - 2; (2)将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶
- pop ax (1)将SS:SP指向的内存单元处的数据送入ax中; (2)SP = SP + 2, SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶
栈满时,使用push执行会发生栈顶超界问题
栈空时,使用pop执行也会发生栈顶超界问题
三、汇编语言程序
- 汇编程序由汇编代码和伪指令组成。没有对应的机器码的指令,最终不会被CPU所执行
- 伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作
1.程序中的三种伪指令
段定义
一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或当作栈空间来使用(一个有意义的汇编程序至少要有一个段,用来存放代码)
定义程序中的段:每个段都需要有段名
end(不是ends)
汇编程序的结束标记。若程序结尾处不加end,编译器在编译程序时,无法知道程序何处结束
assume(假设)
假设某一段寄存器和程序中的某一个用segment…ends定义的段相关联—assume cs:codesg指CS寄存器与codesg关联,将定义的codesg当作程序的代码段使用
2.写出一个程序的过程
- 首先要定义一个段
- 再实现处理任务(汇编指令)
- 指出程序在何结束(end)
- 段与段寄存器关联
- 加上程序返回代码
3.[…]的规定与(…)的约定
[…]——汇编语法规定,表示一个内存单元
举个例子:
mov ax,[bx] ——指的是(ax) = (ds * 16 + (bx))
(…)——为学习方便做出的约定,表示一个内存单元或寄存器中的内容
描述方法:(…)里面只能用寄存器及物理地址
再约定:符号idata表示常量(段前缀)
在[idata]前显示地协商段寄存器(加上段前缀)
mov al,ds[bx] ——与mov ax,[bx]表示一样
小小总结一下:
4.访问连续的内存单元—loop和[bx]
- 作者:Sh4d0w
- 链接:https://sh4d0w.life//article/9fd452a5-f103-49e9-863e-2da3443364f2
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。