assembly2
汇编2
寄存器(不同架构不同)
8086中寄存器均为16位,可存放两个字节(1byte=8bit)。
通用寄存器
-
AX,BX,CX,DX用来存储一般性的数据,被称为通用寄存器。
-
二进制数据在寄存器中是低位存在低地址,高位存在高地址。
-
也可将一个寄存器分为H,L(高8位低8位)来做8位存储器。
字在寄存器中的存储
8086只能一次性处理两种类型数据(寄存器最高只有16位):
-
字节(byte),8bit
-
字(word),16bit,2bytes
(现在大部分架构都能一次性处理双字甚至四字的类型了)
简单汇编指令(MOV & ADD & SUB)
将具体的值记为v,寄存器记为NX,MX...
-
mov(赋值)
MOV NX,v MOV NX,MX
(fir=sec)
-
add(加)
ADD NX,v ADD NX,MX
(fir=fir+sec)
-
sub(减)
(fir=fir-sec)
也可使用AL,AH高低位寄存器进行操作。此时会将他们看作独立的8位寄存器而非AX整体,因此AL产生的进位不会传给AH,而是直接丢弃。
同时需要保证操作数据、寄存器的位数一致。示例:
物理地址(8086下地址计算方法)
x位CPU的含义
如16位CPU:
8086给出物理地址的方法
8086有20根寻址总线,可发送20位地址,寻址能力为1MB(解释:220),然而如果从16位架构CPU内部简单发出,只能发送16位的地址(最大处理单位),寻址能力仅64KB(216),寻找物理地址的能力与一次可发送地址的能力发生矛盾。
8086采用16位短地址 + 16位偏移地址的方式来获得20位地址。
地址加法器计算物理地址的方式是:段地址*16+偏移地址。如CPU欲访问(123C8)16,将会经历以下过程:
- 生成段地址(123C)16
- 生成偏移地址(8)16
- 段地址乘以16(即右移四位,hex中表现为增加一位0)
- 相加,得到(123C8)16
对上述计算方法的理解
由一个基本地址加上一段偏移。
例如描述图书馆的位置,可以先说体育场的位置,再说图书馆距离体育场的位置。
(内存并不分段,分段是CPU内的行为)
段寄存器
段寄存器提供段地址,8086的段寄存器有CS,DS,SS,ES。
CS与IP
CS:代码段寄存器,IP:指令指针寄存器
CS:IP的作用:
设CS中的内容是M,IP中的内容是N,8086CPU将会读取M*16+N地址的一条指令并且执行
或者说,8086任意时刻都将CS:IP指向的内容当作指令执行。
正如上图所示,首先确定基础地址CS:2000,偏移IP:0000H,寻找到B8 23 01三个字节,作为一条指令:
MOV AX,0123H
并执行。之后,自动改变IP:0003H去寻找下一条汇编指令:
MOV BX,0003H
以此类推。每次IP+=此次读取指令的长度。
如何修改CS,IP的值(JMP)
一般改变寄存器都用MOV来传,但CS与IP不能用这种方式来修改。需要使用的指令:jmp指令。指令的格式是:
JMP CS:IP
若仅仅想修改IP的值,可使用:
JMP NX
其中NX为某一合法寄存器。其功能类似MOV IP,NX
理解:jmp形式形似jump,可理解为跳跃到不同的地址去执行其他的指令。
(有点像C里面的goto?)
代码段
代码段是人为划分的一段存储代码(指令)的区域,但是CPU并不认,也就是说不会自动去执行代码段中的代码。想要访问代码段指令的方法是:使用CS:IP指向所定义代码段中第一条指令的首地址。例如代码段为123B0H~123B9H,可以设CS:123BH,IP:0000H
寄存器(内存访问)
字的存储
大小为一个字(2bytes)的数据存储在两个连续的存储单元(1byte),高八位放高地址,低八位放低地址。例如4E20H是一个单字的数据,高八位是4EH,低八位是20H,所以假设存储的内存单元编号为0,1,存储情况应为:0存20H,1存4EH。
如果连续存储多个数据,仅在数据内部进行小端序存储,数据之间不倒序。(IDA分析时经常用到)
DS与[address]
DS段寄存器:用来访问需要访问数据的段地址。
[address]:中括号里的address表示偏移地址,段地址自动选取DS中存储的地址。[]运算的结果是一个内存地址。
由于DS是段寄存器,所以MOV DS,xxx(地址的值)是不合适的,只能采用其他寄存器(如通用寄存器)中转。
例如要访问内存地址为1000:0的数据并存入AL寄存器,可以有如下操作:
MOV BX,1000H
MOV DS,BX
MOV AL,[0]
若要将寄存器中的值存入内存1000:0之中,只需把第三行改为:MOV [0],AL
即可。
mov,add,sub的几种形式
add和sub不能直接对段寄存器进行操作。
数据段
与代码段类似。用DS存段地址,再取偏移。
栈机制
8086中可以将一段内存当作栈来使用。
指令:PUSH AX
表示将寄存器AX的值压入栈中,而POP AX
表示取出(是一种剪切而非复制操作,即弹出栈顶)栈顶元素赋予AX。
栈的形式,低地址为栈顶,高地址为栈底,因此将一个16位数据压入栈时,高位更靠近栈顶。例如将1234H压入栈,那么存储的形式应为:栈顶 34H 12H 栈底。
引申的两个问题:
- 如何确定某一段内存作为栈来操作
- 如何确定某个元素是栈顶元素
使用SS:SP指向栈顶元素。SS是段地址,SP是偏移地址。每次压入一个16位的元素,SP-=2;弹出一个16位的元素,SP+=2。
初始化栈顶示例(10000H~1000FH):
MOV AX,1000H
MOV SS,AX
MOV SP,0010H
栈溢出问题
栈满push和栈空pop会导致栈指针指向栈外部。8086没有提供栈保护机制,只能编程者自己注意。
不过现代的CPU早已实现了高级的栈保护机制。
这让我想起明日方舟:终末地里的一句话:
在塔卫二,矿石病的成本已经大大降低。
尽管这款尚未正式发布的游戏在各种方面存在一些争议,但作为继承本作明日方舟世界观的作品,这句朴实无华的语句将这款游戏,乃至整个泰拉大陆历史的意义推向更高处。
在查询有关现代x86以及x86-64CPU的相关内容时,这句话曾经为我带来的感动重新涌现心头。
或许这就是科技发展的魅力与意义吧。无论当下境遇如何,我们总要相信,人类在这片大地上的艰苦卓绝,筚路蓝缕,会将我们带向更美好的远方。
神经舟u