Fork me on GitHub

汇编语言-02寄存器

寄存器

CPU由运算器、 控制器、 寄存器等器件构成,这些器件靠内部总线相连。内部总线实现CPU内部各个器件之间的联系, 外部总线实现CPU和主板上其他器件的联系。 简单地说, 在CPU中:

  • 运算器进行信息处理;
  • 寄存器进行信息存储;
  • 控制器控制各种器件进行工作;
  • 内部总线连接各种器件, 在它们之间进行数据的传送。

对于汇编程序员来说, CPU中的主要部件是寄存器。 寄存器是CPU中程序员可以用指令读写的部件。 程序员通过改变各种寄存器中的内容来实现对CPU的控制。不同的CPU, 寄存器的个数、 结构是不相同的。

AX――累加器(Accumulator),使用频度最高
BX――基址寄存器(Base Register),常存放存储器地址
CX――计数器(Count Register),常作为计数器
DX――数据寄存器(Data Register),存放数据
SI――源变址寄存器(Source Index),常保存存储单元地址
DI――目的变址寄存器(Destination Index),常保存存储单元地址
BP――基址指针寄存器(Base Pointer),表示堆栈区域中的基地址
SP――堆栈指针寄存器(Stack Pointer),指示堆栈区域的栈顶地址
IP――指令指针寄存器(Instruction Pointer),指示要执行指令所在存储单元的地址。IP寄存器是一个专用寄存器。

CS――代码段寄存器(Code Segment)
DS――数据段寄存器(Data Segment)
SS――堆栈段寄存器(Stack Segment)
ES――附加段寄存器(Extra Segment)

通用寄存器

8086CPU的所有寄存器都是16 位的,可以存放两个字节。AX、BX、EX、DX这4个寄存器通常用来存放般性的数据, 被称为通用寄存器。

以AX为例, 寄存器的逻辑结构

 

 8086CPU的上代CPU中的寄存器都是8位的,为了保证兼容,使原来基于上代CPU编写的程序稍加修改就可以运行在8086之上,8086CPU的AX、BX、EX、DX这4个寄存器都可分为两个可独立使用的8位寄存器来用

  • AX可分为AH和AL;
  • BX可分为BH 和 BL;
  • CX可分为CH和CL;
  • DX可分为DH和DL。

AX――累加器(Accumulator),使用频度最高
BX――基址寄存器(Base Register),常存放存储器地址
CX――计数器(Count Register),常作为计数器
DX――数据寄存器(Data Register),存放数据

AX直接使用

AX分为AH和AL

字在寄存器中的存储

字节: 记为byte,一个字节由8个bit组成, 可以存在8位寄存器中。

字:记为word,一个字由两个字节组成,这两个字节分别称为这个字的高位字节和低位字节。

汇编指令

demo

物理地址

CPU访问内存单元时,要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,我们将这个唯一的地址称为物理地址。
CPU通过地址总线送入存储器的,必须是一个内存单元的物理地址。在CPU向地址总线上发出物理地址之前,必须要在内部先形成这个物理地址。不同的CPU可以有不同的形成物理地址的方式。

16/32/64位的CPU

8086CPU的上一代CPU(8080、8085)等是8位机,而8086是16位机,也可以说8086是16位结构的CPU。

16位结构(16位机、字长为16位等常见说法,与16位结构的含义相同)描述了一个CPU具有下面几方面的结构特性(32位/64位CPU也是这个概念):

  • 运算器一次最多可以处理16位的数据;
  • 寄存器的最大宽度为16位;
  • 寄存器和运算器之间的通路为16位。

8086是16位结构的CPU,这也就是说,在8086内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位的。内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放,对于16位CPU,能一次性处理、传输、暂时存储16位的地址。

CPU给出物理地址

8086CPU有20位地址总线,可以传送20位地址,达到1MB寻址能力。8086CPU又是16位结构,在内部一次性处理、传输、暂时存储的地址为16位。从8086CPU的内部结构来看,如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出的寻址能力只有64KB。8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址

  1. CPU中的相关部件提供两个16位的地址,一 个称为段地址,另一个称为偏移地址;
  2. 段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
  3. 地址加法器将两个16 位地址合成为一 个 20位的物理地址;
  4. 地址加法器通过内部总线将20位物理地址送入输入输出控制电路;
  5. 输入输出控制电路将 20 位物理地址送上地址总线;
  6. 20 位物理地址被地址总线传送到存储器。

地址加法器采用物理地址=段地址x16+偏移地址的方法用段地址和偏移地址合成物理。

每个地址指向的内存单元大小为8bit(1Byte)这个是固定的,所以一个存储单元能存储2位十六进制的数据。内存地址由地址总线宽度决定,每个地址的内容为8位(2位十六进制)

16位CPU访问地址123C8H内存单元

为什么要x16

段地址x16+偏移地址=物理地址本质含义

段地址x16+偏移地址=物理地址的本质含义是:CPU 在访问内存时,用一 个基础地址(段地址x16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。

十六进制x16相当于十六进制左移一位,即二进制左移四位,左移四位后刚好占用20位(数据总线)

段的概念

内存并没有分段,段的划分来自千CPU,由于8086CPU用基础地址(段地址x16)+偏移地址=物理地址 ” 的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。

总结:

CPU访问内存单元时,必须向内存提供内存单元的物理地址。8086CPU在内部用段地址和偏移地移位相加的方法形成最终的物理地址。

CPU可以用不同的段地址和偏移地址形成同一个物理地址。

一个存储单元为8bit即1Byte(固定的),偏移地址16位,变化范围为0-FFFFH,仅用偏移地址(16位)来寻址最多可寻64KB(2^16个内存单元地址 = 2^10*2^6=1024内存单元 * 64)个内存单元。比如给定段地址 1000H, 用偏移地址寻址,CPU 的寻址范围为:100H-l0000H ~ 1FFFFH。
在8086PC 机中,存储单元的地址用两个元素来描述,即段地址和偏移地址。

这里的16位也决定了内存单元的大小,一个内存单元为16位,也就是4位十六进制。

段寄存器

8086CPU 在访问内存时要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址。这里, 需要看一下,是什么部件提供段地址。段地址在8086CPU 的段寄存器中存放。8086CPU 有4 个段寄存器: CS 、DS 、SS 、ES 。当8086CPU 要访问内存时由这4 个段寄存器提供内存单元的段地址。

cs: 代码段寄存器,是 code segment 的缩写;
ds:数据段寄zd存器,是 data segment 的缩写;
ss:堆栈段寄存器, 是 stack segment 的缩写;
es:附加段寄存器,是 extra segment 的缩写;

CS和IP

CS和IP 是8086CPU 中两个最关键的寄存器,它们指示了CPU 当前要读取指令的地址。CS 为代码段寄存器,IP为指令指针寄存器,从名称上我们可以看出它们和指令的关系。

在8086PC 机中,任意时刻,设CS 中的内容为M, IP中的内容为N, 8086CPU将从内存Mx16+N单元开始,读取一条指令并执行。也可以这样表述:8086 机中,任意时刻,CPU将CS:IP 指向的内容当作指令执行。

  1. 8086CPU 当前状态: CS 中的内容为2000H, IP 中的内容为0000H;
  2. 内存20000H~20009H 单元存放着可执行的机器码;
  3. 内存20000H~20009H 单元中存放的机器码对应的汇编指令如下。

地址: 20000H~20002H, 内容:B8 23 01, 长度: 3Byte, 对应汇编指令: mov ax,0123H
地址: 20003H~20005H, 内容:BB 03 00, 长度: 3Byte, 对应汇编指令: mov bx,0003H
地址: 20006H~20007H, 内容:89 D8, 长度: 2Byte, 对应汇编指令: mov ax,bx
地址: 20008H~20009H, 内容:01 D8, 长度: 2Byte, 对应汇编指令: add ax,bx

执行流程

1.初始状态(CS:2000H, IP:0000H, CPU将从内存2000H x16 +0000H 处读取指令执行)

2.CS 、IP 中的内容送入地址加法器(地址加法器完成:物理地址=段地址X 16 +偏移地址)

3.地址加法器将物理地址送入输入输出控制电路

4.输入输出控制电路将物理地址20000H送上地址总线

5.从内存20000H单元开始存放的机器指令B8 23 01通过数据总线被送入CPU

6.输入输出控制电路将机器指令BB 23 01送入指令缓冲器

7.读取一条指令后,IP 中的值自动增加,以使CPU 可以读取下一条指令。因当前读入的指令B82301长度为3个字节,所以IP中的值增加3 。此时,CS:IP 指向内存单元2000:0003

8.执行控制器执行指令B8 23 01(即mov ax,0123H)

9.指令BB 23 01被执行后,AX 中的内容为0123H(此时, CPU 将从内存单元2000:0003 处读取指令)

10.CS:2000H,IP : 0003H(CPU将从内存2000H x16 +0003H处读取指令BB 03 00)

11.CPU从内存20003H处读取指令BB 03 00入指令缓冲器(IP 中的值加3)

12.执行指令BB 03 00(即mov bx,0003H)

13.CPU从内存20006H处读取指令89 D8入指令缓冲器(IP中的值加2)

14.执行指令89 08 (即mov ax,bx)后,AX 中的内容为0003H

15.CPU从内存20008H处读取指令01 D8入指令缓冲器(IP中的值加2)

16.执行指令01 D8(即add ax,bx)后,AX中的内容为0006H。

工作过程总结:

(1) 从CS:IP 指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2) IP=IP+所读取指令的长度,从而指向下一条指令;
(3) 执行指令。转到步骤(1) , 重复这个过程。

在8086CPU 加电启动或复位后(即CPU 刚开始工作时)CS 和lP被设置为CS=FFFFH, IP=0000H, 即在8086PC机刚启动时,CPU 从内存FFFFH 单元中读取指令执行,FFFFH 单元中的指令是8086PC机开机后执行的第一条指令。

CPU将CS:IP 指向的内存单元中的内容看作指令,因为,在任何时候,CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。如果说,内存中的一段信息曾被CPU 执行过的话,那么,它所在的内存单元必然被CS:IP指向过。

修改CS、IP的指令

在CPU 中,程序员能够用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。CPU从何处执行指令是由CS 、IP 中的内容决定的,程序员可以通过改变CS、IP中的内容来控制CPU 执行目标指令。

通用寄存器赋值

mov 指令,如mov ax, 123 将ax 中的值设为123, 显然,我们也可以用同样的方法设置其他寄存器的值,如mov bx,123、mov cx,123、mov dx,123 等。其实,8086CPU 大部分寄存器的值,都可以用mov 指令来改变, mov指令被称为传送指令

但是, mov 指令不能用千设置CS 、IP 的值,原因很简单,因为8086CPU 没有提供这样的功能。8086CPU 为CS 、IP 提供了另外的指令来改变它们的值。能够改变CS 、IP 的内容的指令被统称为转移指令。现在介绍一个最简单的可以修改CS 、IP 的指令: jmp 指令

代码/指令寄存器赋值

同时修改CS 、IP

可用形如“jmp 段地址:偏移地址”的指令完成,如

jmp2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU 将从2AE33H处读取指令。

jmp 3:0B16,执行后:CS=0003H,IP=0B16H,CPU 将从00B46H处读取指令。

“jmp 段地址:偏移地址”指令的功能为:用指令中给出的段地址修改CS, 偏移地址修改IP 。

仅修改IP的内容

jmp 寄存器:用寄存器中的值修改IP(jmp ax, 在含义上好似mov IP,ax,这个指令并不存在!!!)

jmp ax, 指令执行前: ax=1000H, CS=2000H, IP=0003H
             指令执行后: ax=1000H, CS=2000H, IP=1000H
jmp bx, 指令执行前: bx=0B16H, CS=2000H, IP=0003H
             指令执行后: bx=0B16H, CS=2000H, IP=0B16H

代码段

mov ax, 0000 (BS 00 00 )
add ax, 0 123H (05 23 01)
mov bx, ax (SB D8 )
j mp bx ( FF E3 )

这段长度为10 个字节的指令,存放在123B0H- 123B9H 的一组内存单元中,我们就可以认为,123B0H - 123B9H 这段内存是用来存放代码的,是一个代码段,它的段地址为123BH,长度为10 个字节。

将一段内存当作代码段,仅仅是我们在编程时的一种安排,CPU 并不会由于这种安排,就自动地将我们定义的代码段中的指令当作指令来执行。CPU 只认被CS:IP 指向的内存单元中的内容为指令。所以,要让CPU 执行我们放在代码段中的指令,必须要将CS:IP 指向所定义的代码段中的第一条指令的首地址。对于上面的例子,我们将一段代码存放在123B0H -123B9H 内存单元中,将其定义为代码段,如果要让这段代码得到执行,可设CS=123BH、IP=0000H。

总结

(1) 段地址在8086CPU 的段寄存器中存放。当8086CPU 要访问内存时,由段寄存器提供内存单元的段地址。8086CPU有4个段寄存器,其中CS 用来存放指令的段地址。

(2) CS存放指令的段地址,IP存放指令的偏移地址。8086 机中,任意时刻,CPU将CS:IP指向的内容当作指令执行

(3) 8086CPU 的工作过程:
从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;
IP 指向下一条指令;
执行指令。(转到第一步从CS:IP指向的内存单元读取指令,重复这个过程。)

(4) 8086CPU 提供转移指令修改CS 、IP 的内容(jmp)。

查看CPU和内存,用机器指令和汇编指令编程

Debug功能

Debug是DOS、Windows 都提供的实模式(8086 方式)程序的调试工具。使用它,可以查看CPU 各种寄存器中的内容、内存的情况和在机器码级跟踪程序的运行。

  • 用Debug 的R 命令查看、改变CPU 寄存器的内容;
  • 用Debug 的D 命令查看内存中的内容;
  • 用Debug 的E 命令改写内存中的内容;
  • 用Debug 的U 命令将内存中的机器指令翻译成汇编指令;
  • 用Debug 的T 命令执行一条机器指令;
  • 用Debug 的A 命令以汇编指令的格式在内存中写入一条机器指令。

Debug 的命令比较多,共有20 多个,但这6 个命令是和汇编学习密切相关的。

进入Debug

进入dos,运行debug程序,输入r查看所有寄存器的内容

R命令修改寄存器中的值

输入“r ax" 后按Enter 键,将出现“:”作为输入提示,在后面输入要写入的数据后按Enter 键,即完成了对AX中内容的修改。

修改cs/ip中的值

D命令查看内存中的内容

方式一:d 段地址:偏移地址。Debug将列出从指定内存单元开始的128 个内存单元的内容。例如:d 1000:0

方式二:d 段地址:起始偏移地址  结尾偏移地址。DEBUG从起始位置开始一直显示到结束位置。例如:d 1000:0 9。

方式三:d 段地址:起始偏移地址 L长度,长度以L参数为标识。DEBUG从起始位置开始显示指定长度的内容。例如:d 1000:0 L9。

E命令改写内存

e 段地址:偏移地址 数据 数据 数据……

 输入e 1000:10, 按Enter 键。

e 段地址:偏移地址,逐个修改

Debug 显示起始地址1000:0010, 和第一单元(即1000:0010 单元)的原始内容,然后光标停在“.”的后面提示输入想要写入的数据,此时可以有两个选择: 其一为输入数据, 然后按空格键,即用输入的数据改写当前的内存单元; 其二为不输入数据,直接按空格键,则不对当前内存单元进行改写。
当前单元处理完成后(不论是改写或没有改写,只要按了空格键,就表示处理完成), Debug 将接着显示下一个内存单元的原始内容,并提示进行修改,读者可以用同样的方法处理。所有希望改写的内存单元改写完毕后,按Enter 键, E 命令操作结束。

用E命令写入字符

用E命令从内存1000:0 开始写入数值1、字符“a"、数值2、字符"b"、数值3、字符“C"'。字符“a"、“b"、"C"的ASCII码十六进制值:61H、62H、63H。

用E命令将机器码写入内存

机器码也是数据,可以用E命令将机器码写入内存。

U命令将内存单元中的内容翻译为汇编指令

T命令执行CS:IP指向的指令

首先6个存储单元的机器码写入内存1000:0000,并查看机器码对应的汇编指令。

修改cs和ip中存储的段地址和偏移量,并查看CS、IP寄存器中的地址。以及AX、CX寄存器中的值。

T指令运行CS:IP指向的指令数据,执行完后IP偏移量会自动移动到下一个。

A命令以汇编指令的形式在内存中写入机器指令

debug指令总结

  • 查看、修改CPU 中寄存器的内容: R 命令
  • 查看内存中的内容: D 命令
  • 修改内存中的内容: E 命令(可以写入数据、指令,在内存中,它们实际上没有区别)
  • 将内存中的内容解释为机器指令和对应的汇编指令: U 命令
  • 执行CS:IP 指向的内存单元处的指令: T 命令
  • 以汇编指令的形式向内存中写入指令: A 命令
posted @ 2020-05-01 18:31  秋夜雨巷  阅读(1150)  评论(0编辑  收藏  举报