寄存器与内存
寄存器及数据存储
8086寄存器(寄存器都是16位的)
通用寄存器:AX、BX、CX、DX
变址寄存器:SI、DI
指针寄存器:SP、BP
指令指针寄存器:IP
段寄存器:CS、SS、DS、ES
标志寄存器:PSW
兼容性问题(16位寄存器兼容旧机器的8位寄存器)
由于兼容问题的产生,因此通用寄存器被分为两部分,如AX的高八位又被称作AH寄存器,低八位被称作AL寄存器。另外三个通用寄存器同理。
| 寄存器名称 | 高八位(8-15) | 低八位(0-7) |
|---|---|---|
| AX | AH | AL |
| BX | BH | BL |
| CX | CH | CL |
| DX | DH | DL |
8086是16位CPU,因此它的字长为16位,一个字可以存储在一个16位寄存器中
1)这个字的高位字节存储在这个寄存器的高八位寄存器
2)这个字的低位字节存储在这个寄存器的低八位寄存器
MOV和ADD指令
指令应用范例
| 汇编指令 | 控制CPU完成的操作 | 高级语言描述 |
|---|---|---|
| mov ax,18 | 将18送入AX | AX = 18 |
| mov ah,78 | 将78送入AH | AH = 78 |
| add ax,8 | 将寄存器AX中的值加上8 | AX = AX + 8 |
| mov ax,bx | 将寄存器BX中的数据送入寄存器AX | AX = BX |
| add ax,bx | 将AX,BX中的内容相加,结果存入AX中 | AX = AX + BX |
需要注意(如果执行下述两条命令,假设AX寄存器初始值为0000H)
add al,FFH
add al,FFH
其结果为EF,而不是1EF,而AX寄存器值为00EFH
mov指令操作数据(不需背诵)
| 指令形式 | 示例 |
|---|---|
| mov 寄存器,数据 | mov ax,8 |
| mov 寄存器,寄存器 | mov ax,bx |
| mov 寄存器,内存单元 | mov ax,[0] |
| mov 内存单元,寄存器 | mov [0],ax |
| mov 段寄存器,寄存器 | mov ds,ax |
| mov 寄存器,段寄存器 | mov ax,ds |
| mov 内存单元,段寄存器 | mov [0],ds |
| mov 段寄存器,内存单元 | mov ds,[0] |
确定物理地址的方法
物理地址
1)cpu访问内存单元时要给出内存单元的地址
2)所有的内存单元构成的存储空间是一个一位的线性空间
3)每一个内存单元在这个空间中都有唯一的地址,这个唯一的地址称为物理地址
8086的尴尬
1)8086有20根地址总线,寻址能力为1MB
2)8086是16位结构的cpu,一次性处理的数据最大宽度为16
3)因此在传递地址上,出现了矛盾
解决矛盾方法
利用两个16位的地址(段地址,偏移地址),合成一个20位的物理地址
地址转换公式
物理地址 = 段地址 * 16 + 偏移地址
实例:
20100H = 2000H * 16 + 0100H
根据上述公式,可以简单的理解到,段地址一定是16的倍数(废话),并且物理地址的确定可以通过不同的段地址与偏移地址构成,类似 6 = 1+5 与 6 = 2+4等,因此物理地址的计算过程不唯一。
内存的分段表示法
8086用“段地址 * 16 + 偏移地址”来计算物理地址的方法获取真实的物理地址。但是内存并没有真正的分段,段的划分来自于CPU
段划分
8086的地址线为20根,将二十个二进制转换为十六进制就是五位(下方横线代替五位十六进制)
_ _ _ _ _
由于物理地址的计算公式是“段地址 * 16 + 偏移地址”,因此每次分配段大小时,都要一次性分配四位二进制位,也就是一位十六进制位
如果我们将前一位当成段地址
_ | _ _ _ _
该情况下
段取值范围: 0-F
偏移取整范围:0000-FFFF
如果我们将前两位当成段地址
_ _ | _ _ _
该情况下
段取值范围: 00 - FF
偏移取值范围:000 - FFF
其余情况同上述规律,因此我们可以观察到,段的划分由我们或者说由cpu决定,真实的物理空间并没有它所代表的含义而变大或者变小。
在寻址过程中,段地址与偏移地址组合形成物理地址,但是偏移地址的最大值不能超过64K,因为寄存器是16位的寄存器
综上:我们经常应用情况一,来作为平时书写物理地址的格式:(2000:1F60)
2 0 0 0 (段地址)
+ 1 F 6 0 (偏移地址)
---------------
2 1 F 6 0 (物理地址)
段地址很重要,因此用专门存放段地址的寄存器
1)CS - 代码段寄存器
2)DS - 数据段寄存器
3)SS - 栈段寄存器
4)ES - 附加段寄存器
DEBUG的使用
Debug是什么?
debug是dos系统中著名的调试程序,也可以运行在windows系统模式下。可以查看cpu中的内容、内存情况,并且在机器指令级跟踪程序的运行。
DEBUG常用命令
| 命令 | 功能 |
|---|---|
| R | 查看、改变CPU寄存器的内容 |
| D | 查看内存中的内容 |
| E | 改变内存中的内容 |
| U | 将内存中的机器指令翻译成汇编指令 |
| A | 以汇编指令的格式在内存中写入机器指令 |
| T | 运行机器指令 |
启动debug
1)打开dosbox,输入指令,将masm文件夹挂载到C盘
mount c e:\masm

e:\目录下存放编译汇编程序的文件,才会将这个文件夹挂载到dosbox的c盘

2)直接输入debug,显示“-”符号,证明进入到了debug环境中,如下图

3)尝试用r命令查看寄存器内容:

更改寄存器内容
r 寄存器名称

4)利用d命令查看内存中的内容,之前提到过的段地址与偏移地址的书写格式,是个人习惯,但是物理地址计算公式是恒定不变的

查看指定位置的数据内容
d 段地址:偏移地址

限定查看的数据范围,比如限定只显示偏移地址从 100 到 10F
d 段地址:偏移地址 结尾偏移地址

注意:利用d查看指定地址之后,直接输入d命令执行,其显示的内容为,指定地址的后序地址内容,地址是连续的

5)利用E命令,改变指定内存位置的数据内容,如下操作:
e 段地址:偏移地址 数据1 数据2 数据3 ...

利用e的逐位询问的修改方法(如果旧数据比对成功,则进行修改)
逐个询问式修改:该模式下,每次修改数据前,系统会显示当前地址下的数据值大小
应用格式:
e 段地址:偏移地址
操作:
空格:继续修改操作
回车:结束修改

6)用U命令将内存中的机器指令翻译成汇编指令
汇编指令
mov ax,0123H
mov bx,0003H
mov ax,bx
add ax,bx
对应机器指令
B8 23 01
BB 03 00
89 D8
01 D8
e 地址 数据 (写入)
d 地址(查看)
u 地址(查看代码)

7)用A命令以汇编指令的格式在内存中写入机器指令
有汇编指令
mov ax,0123H
mov bx,0003H
mov ax,bx
add ax,bx
对应机器码
B8 23 01
BB 03 00
89 D8
01 D8
a 地址(写入汇编指令)
d 地址(查看数据)
u地址(查看代码)


8)T指令执行CS:IP处的指令,并且显示执行后寄存器的值

9)Q命令退出debug
CS、IP与代码段
CS:代码段寄存器
IP:指令寄存器
8086PC工作过程的简要藐视
1)从cs:ip指向内存单元读取指令,读取的指令进入指令缓冲器
2)ip = ip + 所读取指令的长度,从而指向下一条指令
3)执行指令,转到步骤1,重复这个过程
如下操作,改变cs、ip的地址指向,利用t命令进行指令的执行

jmp指令
修改cs、ip的指令
执行何处的指令,取决于cs:ip
可以通过改变cs:ip中的内容,来控制cpu要执行的目标指令
改变IP的方法
1)通过调试r命令改变IP值
2)通过cpu自主运行,每执行完一条指令,ip值自行加刚刚执行指令长度值
3)通过jmp指令跳转指定地址,cpu更改cs:ip值
转移指令
1)同时修改cs:ip的内容
功能:用指令中给出的段地址修改cs,偏移地址修改ip
jmp 段地址:偏移地址
如:
jmp 2AE3:3
jmp 3:0B18
2)仅修改ip的内容,类似于:mov ip,ax
jmp 某一合法寄存器
如:
jmp ax
jmp bx
操作实验
1)准备两段代码
1000:0
mov ax,123
jmp ax
1000:123
mov bx,456
add ax,bx
2)利用a命令将两段代码写入

3)改变cs:ip的值,设置代码执行的初始位置

4)开始执行,观察跳转数据变化

5)执行过程与代码内容相符,在发生跳转时,ip数据从0003变为了0123,ip值改变,下一条命令读取地址被改变
内存中字的存储
字概述
字是CPU一次所能处理的数据大小,32位CPU一次所能处理的数据为32位,因此其字大小为32位。同理,8086CPU是16位CPU,因此字大小为16位
16位的字如何在寄存器中存储
数据存储方式:高八位放在高字节,低八位放在低字节
观察mov ax,1234指令的存储方式
1)用a指令存储数值
2)用d指令读取指定内存地址的数据
3)观察1234在内存中的存储

5)现象与原理相符
额外小知识:(存储模式)
小端存储:低位数据放在低位字节,高位数据放在高位字节(符合理论现象)
大端存储:低位数据放在高位字节,高位数据放在低位字节
用DS和[address]实现字的传递
要求
CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址
原理
在8086CPU中,内存地址由段地址和偏移地址组成(段地址:偏移地址)
解决方案
DS和[address]配合
mov bx,1000H
mov ds,bx
mov al,[0]
需要注意的点
1)立即数(如1000H、1234H等数值被称作立即数) 可以直接赋值给通用寄存器,而不能直接赋值给ds寄存器,因此需要两次mov来对ds初始化
2)如果"[ ]"中没有明确段地址大小,那么默认使用ds中的数据当做段地址,因此[0]等同于[ds:0]
操作观察
1)设置1000:0位置的数据依次为11,22,33
2)利用下述命令操作观察
mov ax,1000
mov ds,ax
mov ax,[0]
mov bx,[2]
mov cx,[1]
add bx,[2]
add cx,[2]

值的变化过程


栈操作的实现
栈结构
1)是一种只能在一端进行插入或删除操作的数据结构。
2)栈的基本操作
入栈:将一个新的元素放置栈顶
出栈:从栈顶取出一个元素
3)栈顶的元素总是最后入展,需要出栈时,有最先被去除
栈指令(针对字进行操作,不是字节操作)
push:入栈
栈顶指针向低地址偏移,数据存入栈顶指针指向位置
pop: 出栈
读取数据,栈顶指针向高位地址偏移
8086CPU中,两个与栈相关的寄存器
1)栈段寄存器(SS):存放栈顶的段地址
2)栈顶指针寄存器(SP):存放栈顶的偏移地址
汇编代码
mov ax,1000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH
push ax
push bx
pop ax
pop bx

浙公网安备 33010602011771号