1.  CPU中的运算器做信息处理;寄存器进行信息存储;控制器控制各种器件进行工作;内部总线连接各器件,在它们之间进行数据的传送。对于汇编程序员来说,CPU中的主要部件是寄存器。寄存器是CPU中程序员可以用指令读写的部件,并通过改变各种寄存器的内容来实现对CPU的控制。

8086CPU(16位CPU)有14个寄存器,分别是:AX,BX,CD,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES和PSW。

每个寄存器又可分成两个寄存器,一个是高位,一个是低位。如AX,可分成AH,AL两个寄存器,如下图:

android studio 寄存器值 寄存器ah_IP

AX的低8位(0位-7位)构成了AL寄存器,高8位(8位-15位)构成了AH寄存器。AH和AL是可以独立使用的8位寄存器。存储形式如下图:

android studio 寄存器值 寄存器ah_IP_02

android studio 寄存器值 寄存器ah_寄存器_03


android studio 寄存器值 寄存器ah_寄存器_04

一个16位寄存器所能存储数据的最大值是:2 ^ 16 - 1 = 65535,8位寄存器所能存储数据的最大值是:2 ^ 8 – 1 = 255

 

2.  mov, add 指令

格式:

  mov 目标寄存器, 源数据(可以是常量值或寄存名)   ; 将源数据存入目标寄存器中
  add 目标寄存器,源数据(可以是常量值或寄存名)   ; 将源数据与目标寄存器中的数据相加,结果存入目标寄存器中
如:

mov ax, 18    ; 将18送入寄存器AX
mov ah, 78    ; 将78送入寄存器AH
add ax, 8      ; 将寄存器AX中的数值加上8
mov ax, bx   ; 将寄存器BX中的数据送入寄存器AX
add ax, bx    ; 将AX和BX中的数值相加,结果存在AX中

注:汇编语言中不区分大小写,mov ax, 18 与 MOV AX, 18 是等价的

 

3. 物理地址

8086是16位结构的CPU,有如下结构特性:
1.) 运算器一次最多可处理16位的数据
2.) 寄存器的最大宽度为16位
3.) 寄存器的运算器之间的通路为16位

而8086 CPU的地址总线是20位的,可以传送20位地址,寻址能力是1MB。而CPU内部结构是16位的,寻址能力只有64KB,具体如下图:

android studio 寄存器值 寄存器ah_寄存器_05

上图的8086 CPU读写内存示意图分如下步骤:
1.) CPU提供两个16位的地址,一个为段地址,一个为偏移地址;
2.) 段地址和偏移地址通过内部总线送入地址加法器;
3.) 地址加法器将两个16位地址合成为一个20位的物理地址;
4.) 地址加法器再通过内部总线把20位的物理地址送入输入输出控制电路;
5.) 输入输出控制电路将20位物理地址送上地址总线;
6.) 最后, 20位物理地址被地址总线传送到存储器

 

4. 物理地址的求法

地址加法器将两个16位地址合成为一个20位物理地址的方法是:
物理地址 = 段地址 * 16 + 偏移地址
如:8086 CPU要访问地址为:123C8H的内存单元,地址加法器所做的工作如下(以下地址均以16进制表示):
1. CPU先将123C8H 转换成段地址和偏移地址,段地址对应为1230,偏移地址对应为00C8,并传入给地址加法器;

2. 加法器将段地址*16,结果为:1230 * 16 = 12300 ;

3. 将段地址乘16后的结果与偏移地址相加:12300 + 00C8 = 123C8,得到物理地址 ;

4. 输出物理地址:123C8

段地址 * 16 + 偏移地址 = 物理地址的本质含义是:CPU在访问内存时,用一个基础地址(段地址 * 16)和一个相对基础地址的偏移地址相加,给出内存单元的物理地址。这也是8086 CPU的寻址方式,它以段地址 * 16看作是基础地址。

 

5. 段

事实上,内存并没有分段,段的划分来自于CPU,CPU通过分段的方式来管理内存。而采用分段管理内存的依据也是来自于CPU的寻址方式,即:物理地址 = 段地址 * 16 + 偏移地址。根据段地址 * 16为基础地址,一个段的起始地址也一定是16的倍数;偏移地址是16位,16位的寻址能力为64KB,所以一个段的长度最大为64KB

而下图说明:CPU可以用不同的段地址和偏移地址形成同一个物理地址

android studio 寄存器值 寄存器ah_IP_06


再根据段的最大寻址能力为64KB,假如有段地址为:1000H,用偏移地址寻址,CPU的寻址范围为:1000H~1FFFFH。

在8086PC机中,存储单元的地址用两个元素来描述,即段地址和偏移地址。所以,如果“数据在21F60H内存单元中”,的说话有两种:1.) 数据存在内存2000: 1F60单元中;2.) 数据存在内在的2000段中的1F60单元中。

 

 

6. 段寄存器

8086CPU有4个段寄存器:CS,DS,SS,ES。

1.) CS和IP
CS和IP是8086CPU中两个最关键的寄存器,它们指针了CPU当前要读取指令的地址。CS为代码段寄存器,IP为指令指针寄存器,从名称上可以看出它们和指令的关系。
在8086PC机中,任意时刻,设CS中的内容为M,IP中的内容为N,8086CPU将从内存M * 16 + N单元开始,读取一条指令并执行。也可表述为:CPUCSIP指向的内容当作指令执行

8086CPU工作过程:
1.)从CS:IP指向内在单元读取指令,读取的指令进入指令缓冲器;
2.)IP = IP + 所读取指令的长度,从而指向下一条指令;
3.)执行指令。转到团聚1.),重复这具过程

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

在这里,CS和IP为CPU提供了所要执行指令的地址。

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

 

2.) 修改CS、IP的指令

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

8086CPU大部分寄存器的值,都可用mov指令来修改,mov指令也被称为传送指令。但mov指令却不能用于修改CS, IP的值,原因是8086CPU没有提供这样的功能。用于修改CS, IP的指令被称为转移指令,即:jmp指令。

若想同时修改CS, IP的内容,可用指令“jmp 段地址: 偏移地址”来完成,如:
jmp 2AE3:3, 执行后:CS = 2AE2H, IP = 0003H, CPU将从2AE33H处读取指令。
jmp 3:0B1B, 执行后:CS = 0003H, IP = 0B16H,CPU将从00B46H处读取指令。
jmp 段地址: 偏移地址”的指令功能是:用指令中给出的段地址修改CS, 偏移地址修改IP。

若想仅修改IP的内容,可用指令“jmp 某一合法寄存器” 完成,如:
jmp ax, 指令执行前:ax = 1000H, CS = 2000H, IP = 0003H
        指令执行后:ax = 1000H, CS = 2000H, IP = 1000H

jmp bx, 指令执行前:bx = 0B16H, CS = 2000H, IP = 0003H
        指令执行后:ax = 0B16H, CS = 2000H, IP = 0B16H
指令“jmp 某一合法寄存器”的功能是:用寄存器中的值修改IP

jmp ax, 相当于mov IP, ax 这样的指令。当然,前面已经说到mov指令是不能用于修改CS, IP的,这里只是类比说明。

如果有段代码需要CPU执行,就要让CS、IP指向这块代码所存储的内存地址的首地址。

 

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

Debug常用命令:
1.)R命令查看/修改CPU寄存器内容;
2.)D命令查看内存中的内容;
3.)E命令修改内存中的内容;
4.)U命令将内存中的机器指令翻译成汇编指令(反汇编);
5.)T命令机器指令跟踪;
6.)A命令以汇编指令的格式在内存中写入一条机器指令;

 

1.)R命令格式:R {寄存器名}  (注:{} 表参数为可选,下同)

   

android studio 寄存器值 寄存器ah_段地址_07

 

以上执行了r,r ax,1111,r命令,

    r 先查看CPU寄存器中的内容;

    r ax 查看/修改(此时也可修改寄存器)寄存器ax中的内容;

    1111 输入需修改的值

    r 查看CPU寄存器中内容

 

2.)D命令格式:D {段地址: 偏移地址}

   

android studio 寄存器值 寄存器ah_寄存器_08

    要查看内在10000H处的内容,先将这个地址表示为段地址:偏移地址的格式,可以是:1000:0,然后用“d 1000:0”列出在1000:0处的内容。

    上面输出分3部分,中间部分是从指定的地址开始的128个内存单元的内容,用16进制的格式输出,每行输出从16的整数倍的地址开始,最多输出16个单元的内容。注意在每行的中间有一个‘-’,它将每行的输出分为两部分,这样便于查看。‘-’的左边是高段,右边是低段。

    左边是每行的起始地址。

    右边是每个内存单元中的数据对应可显示的ASCII码。

在使用“d 段地址: 偏移地址”后,再接着使用d命令,可列出以偏移量(前一条d 段地址: 偏移地址中的偏移量)为单元显示后面内存地址中的内容。如:

   

android studio 寄存器值 寄存器ah_段地址_09

也可以查看指定内存地址范围内的内容“d 段地址: 起始偏移地址 结尾偏移地址”,如要查看1000:0 ~ 1000:9,命令为:d 1000:0 9:

   

android studio 寄存器值 寄存器ah_IP_10

android studio 寄存器值 寄存器ah_段地址_11

如果想查看内在单元10000H中的内容,可以让“段地址: 偏移地址 ”都表示10000H这个物理地址,命令为:d 1000:0 0:

   

android studio 寄存器值 寄存器ah_段地址_12

 

3.)E命令格式:E 起始地址 数据 数据 数据 …

    如果要将内存1000:0 ~ 1000:9 单元中的内容改写为:0, 1, 2, 3, 4, 5, 6, 7, 8, 9,命令为:e 1000:0 0 1 2 3 4 5 6 7 8 9

   

android studio 寄存器值 寄存器ah_android studio 寄存器值_13

    当然也可一个一个地改写内存中的内容,如:

   

android studio 寄存器值 寄存器ah_IP_14


    以上在输入e 1000:10后,会提示在"B0.”后输入你想修改的数据,此时空格,提示下一个内存单元想要输入的数据。如果还想继续输入,可继续空格。如果要跳过某个内存单元,可直接空格,跳到下一单元。

 

    如果想对1000:0, 1000:2, 1000:4单元内写入数据1,2,3,对1000:1, 1000:3, 1000:5单元内写入字符'a', 'b', 'c',命令为:e 1000:0 1 'a' 2 'b' 3 'c': 

   

android studio 寄存器值 寄存器ah_IP_15

 

    如果想从1000: 0开始写入:1, 字符串"a+b", 2, 字符串"c++", 3, 字符串"IBM",命令为:e 1000:0 1 "a+b" 2 "c++" 3 "IBM":

   

android studio 寄存器值 寄存器ah_android studio 寄存器值_16

 

4.)U命令格式:U 段地址

    如果要查看e 1000:0 b8 01 00 b9 02 00 01 c8 这条指令翻译成汇编(反汇编)指令,可以用命令:U 1000:0

  

android studio 寄存器值 寄存器ah_android studio 寄存器值_17

 


  U命令的输出分为3部分:最左边的是机器指令的地址,中间是机器指令,右边是机器指令所对应的汇编指令。

 

5.)T命令格式:T {段地址: 偏移地址}

    如果简单执行T命令,可执行CS: IP指向命令:

   

android studio 寄存器值 寄存器ah_android studio 寄存器值_18

  上图中,先是E命令从1000:0开始的内存单元里写入8个字节的机器码,再用R命令查看CPU中寄存器的状态,此时是CS = 1383, IP = 0100。接着再修改CS,IP的指向,使CS:IP 指向1000:0。再使用T命令跟踪前面的命令,执行后的AX中的内容被改写为1,IP改为IP + 3(mov ax, 0001的指令长度为3个字节),CS:IP指向下一指令。

 

6.)A命令格式:A {段地址:偏移地址}

   

android studio 寄存器值 寄存器ah_段地址_19


    A命令写入汇编指令时,在给出的起始地址后按ENTER键结束操作。

    当然,你也可以直接使用A命令来给一直预定的地址(这个预定地址可以用R命令来查看)写入汇编指令,如:

   

android studio 寄存器值 寄存器ah_段地址_20