2 访问寄存器和内存

学习笔记
作者: MingXiao

2.1 寄存器和数据存储

8086的CPU和内存

其中EU(Execution Unit)BIU(Bus Interface Unit)

8086有14个寄存器

  • 通用寄存器AX,BX,CX,DX都可以分为高位和低位
  • CS:IP是存放指令的地址组合

所有寄存器都是16位的,可以存放两个字节,上述四个都可以分为两个独立的八位寄存器使用(AX=AH+AL)

8086是16位字长(1个字),一次可以处理一个寄存器的内容,一个寄存器存储一个字

2.2 MOV和ADD指令

汇编指令 控制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 += bx

汇编指令不区分大小写

例:ax=8226h,bx=8226h,执行add ax, bx,得到ax=?

解:ax=044ch,本来是1044ch,但是ax只有16位,去掉msb

例:ax=00c5h,bx=8293h,执行add al, bl,得到ax=?

解:ax=0058h,本来是0158h,但是al只有8位,去掉msb

2.3 确定物理地址

所有的内存单元构成的存储空间是线性的(C++的数组)

8086的寻址能力是\(2^{20}\),但是它是16位机,只能一次处理16bit,需要使用地址加法器合成物理地址

合成方法:段地址*16+偏移地址=物理地址

都是16进制表示,*16表示末尾加一个0,转为2进制就是多0000,变成了20bit

8086处理物理地址的过程

2.4 内存分段表示

内存本身是连续的,段的划分来自CPU(就是段地址的划分)

段地址由于16,所以*段的起始地址一定是16的倍数,在二进制中表现为0000结尾;偏移地址为16bit,故每个段最大长度\(2^{16}=64\)kB

不同的段地址和偏移地址可以表示一个物理地址,如2000h*16+1F60h=2100h*16+0F60h

2.5 Debug常见命令

R(Register)查看、改变寄存器的内容;R 寄存器名 改变指定寄存器内容(-r ax /endl ax 1234,ax=1234)

D(Data)查看内存内容,列出预设地址内存的128个字节(8*16,一个地址2bit)的内容

  • -d 段地址:偏移地址 (结尾偏移地址)列出指定位置的内存内容

E(Edit)改变内存内容

  • -e 段地址:偏移地址 12 34 将起始位置开始的两个地址内容改为12 34

U将内存中的机器指令翻译为汇编指令

A以汇编指令的格式在内存里写指令

  • -a 段地址:偏移地址 汇编指令

T执行机器指令

  • -t 执行CS:IP处的指令,每次执行一条

2.6 CS、IP和代码段

CS:代码段寄存器

IP:指令指针寄存器

CS:IP:CPU将该地址指向的内容当作指令(不当作数据)

8086CPU的指令读取和执行

  1. 从CS:IP指向的单元读取指令,指令进入指令缓冲器
  2. IP += 上一段指令的长度(如 mov ax, 8长3个字节,ip从0100变为0103)
  3. 执行这段指令

重复上面三个步骤(一直-t)

2.7 JMP指令

用于修改CS和IP的值,改变程序执行的顺序

修改CS/IP的方式

  1. -rcs+值,-rip+值;但这是debug内部调试手段
  2. mov cs,2000H;错误,特殊的寄存器不能用mov
  3. 转移指令jmp
    1. jmp 段地址:偏移地址:修改CS:IP,用于段间程序跳跃
    2. jmp 某个值或寄存器:仅修改IP,用于段内程序跳跃

例:如图,从20000H开始执行,执行的序列是?

  1. mov ax, 6622H;ax=6622H
  2. jmp 1000:3;段间跳跃
  3. mov ax, 0000H;ax=0000H
  4. mov bx, ax;bx=0000H
  5. jmp bx;段内跳跃
  6. mov ax, 0123H;ax=0123H
  7. mov ax,0000H;ax=0000H,开始循环

2.8 内存中字的储存

16bit=2byte=1word,1word的高八位放在高字节,低八位放在低字节

如"4E20H,0012H",存放方式是

0 1 2 3
20H 4EH 12H 00H

字节型和字型

字节型指一个字节;字型指从这个字节开始的2个字节,这个字节为低八位

如上的内存,1地址存放的字节型数据是“4EH”,1地址存放的字型数据是“124EH”

但是,每次读内存段开始的一个字节

图中就是偶数开始,故读0地址的字型数据要1次,读1地址的字型数据要2

2.9 用ds寄存器和[address]实现字的传送

ds(data segment)寄存器存放的是当前指向的段地址信息,[address]表示偏移地址

下面代码:

mov bx, 1000H
mov ds, bx
mov al, [0] #等价于 mov al, ds:[0]

由于mov无法直接操作ds,需要一个中间变量;bx表示base,常用来存放地址的中间变量

上面对代码将段地址写为1000H,将1000H:0000H的数据写入al中(也可以用ax)

例:

注意ax是字型变量,al是字节型变量,故所有的mov操作都将内存地址视为字型变量的低地址

数据段

默认由ds寄存器的值决定,偏移地址由[address]决定

数据累加问题

看以下的区别:

mov ax, 123BH mov ax, 123BH

mov ds, ax mov ds, ax

mov al, 0 mov ax, 0

add al, [0] add ax, [0]

add al, [1] add ax, [2]

add al, [2] add ax, [4]

左边是用一个字节累加,是字节型累加;右边用2个字节累加,是字型累加

2.10 mov和add的指令形式

mov指令 示例 add指令 示例
mov 寄存器, 数据 mov ax, 8 add 寄存器, 数据 add ax, 8
mov 寄存器, 寄存器 mov ax, bx add 寄存器, 寄存器 add ax, bx
mov 寄存器, 内存单元 mov ax, [0] add 寄存器, 内存单元 add ax, [0]
mov 内存单元, 寄存器 mov [0], ax add 内存单元, 寄存器 add [0], ax
mov 段寄存器, 寄存器 mov ds/cs, ax / /

sub用法与add相同

2.11 栈与栈操作的实现

栈:先进后出

栈段(stack segment, ss)是专门开辟的一个区域,用于暂存数据(如函数调用等)

栈的基本操作:push入栈,pop出栈

8086中栈的操作

CPU对栈的指定:SS寄存器存放栈顶的段地址,SP(stack pointer)存放栈顶的偏移地址

栈指令操作:push ax和pop ax,没有push al和pop al

任意时刻,SS:SP指向栈顶元素;栈顶的地址最低

指定栈空间

mov ax, 1000H

mov ss, ax

mov sp, 0010H

ss不能mov,但是sp可以

栈空间范围:SS:0000H——SS:SP-1,由于SP最大64K,一个栈最大空间为64KB

栈段和数据段可以共享段地址

push和pop指令

push ax:将ax值送入SS:SP;SP -= 2

pop ax:将SS:SP中的值送入ax;SP += 2

push和pop无法保证指针不出栈,要自己注意



Comments