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存段地址,再取偏移。

rnm

栈机制

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

posted @ 2024-10-24 20:51  brs7  阅读(10)  评论(0编辑  收藏  举报