汇编

 · 2024-10-11 · 次阅读


汇编语言的组成:

1.汇编指令(机器码的助记符)//直接等价于特定的机器指令,所以反编译一般是汇编

2.伪指令(编译器执行)//没有对应的机器码,和其他符号一样,都是编译器实现的

3.其他符号(编译器识别)//转化成逻辑运算

存储器:

指令和数据都是在存储器中存放,也就是内存(磁盘内的数据得先到内存中才能被使用)

虚拟内存,就是用部分硬盘当作内存用(18年也不至于是sata2吧)

存储单元:

//包括缓存

被划分为若干个存储单元,每个单元从0开始编号

制图时从上到下,由低地址到高地址,但是读起来还是从高开始(忽略第一个1之前的0)

单位就不必说了,最后那个B代表Byte

CPU对存储器的读写:

三类信息交互

1.地址信息

2.控制信息

3.数据信息

CPU与内存之间的总线,实际上就是包含了这三者,即地址总线,数据总线,控制总线(逻辑划分)。

它们都是线性排列的

指令与数据:

同样的二进制信息,可以是数据,也可以是信息。具体当作什么,取决于它是在哪种总线上

1.地址总线

指定存储单元,总线能传输多少,就能对多少个存储单元进行寻址。

(64位,32位说的就是地址总线的宽度)//64bit,即8byte

要实现64位,CPU,OS,软件都必需是64位的,这是因为向下继承

内存会自动定位从地址总线来的地址

有N根地址总线位宽就是N(其实就是Nbit),一位最多可以指定2^n个内存单元

2.数据总线

宽度越大,传输速度越快

注意,索引从低到高,传输数据是从低位开始,也就是数据的后面一段(多次传输时)

3.控制总线

操作只有读和写(1/0),控制总线是个总称,数量越多,意味着CPU可以控制的外部元器件的种类越多,有多少根,就有多少种

内存读写是几根控制线综合发出的(有俩,读/写信号输出控制线)

内存地址空间:

大小就是CPU的地址线宽度可寻得的内存单元个数

主板,接口卡,声卡(其实不是没了),RAM,ROM(就是BIOS的那个,可以刷)

CPU看到的其他元器件:

1728139697850

其实就是看成逻辑存储器,所有内存单元构成的存储空间是一维线性空间,每个内存单元都有唯一的地址,即物理地址

内存空间地址段分配:

//实际上是在分配CPU的总线//

不同设备有自己分配到的地址段,各部分具体大小看CPU。其实就是将所有的存储器统一起来。

寄存器(CPU工作原理)

CPU概述

由运算器,控制器,寄存器组成,也是要以总线相连

通用寄存器

一共有14个:(16位)

AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW

它们甚至在缓存之前。寄存器实际上不只14个,指令集也会有自己的寄存器。

寄存器都是16位的2字节,也就是1字

更古早的U寄存器只有8位,为了兼容,现在的寄存器可以分成俩八位来用

比如AX可以分为AHAL。BH,CH,DH同理

字在寄存器中的存储

数据:18
二进制表示:10010
在AX中:0(高位)000000000010010(低位)//可见,寄存器最大只能存储2^16-1
写成16进制表示:0012H
//H代表是十六进制,只有4个十六进制数(因为1个十六进制数对4个二进制数,即2^4=16),也有0x0012的表示法

1字就是16bit,2byte(再次重申),现在CPU就是一次一字

汇编指令:

//不区分大小写

mov ax,18   //把18送入ax寄存器中   即ax=18      mov:传送指令
add ax,8 /将ax中的值加上8 即ax=ax+8
mov ax,bx //将bx中的数据送入a下 即ax=bx,注意是后面赋到前面
add ax,bx //将ax与bx中的相加,并存在ax中 即ax=ax+bx
add ax,4E20H //什么进制都行,八进制前面加0,二进制最后加B,十进制都不加

add可能导致上溢

add al,85H    //只加在了低8位,即后面的那俩,这时如果发生上溢,也是直接舍弃

CPU给出物理地址的方法:

以8086为例:

1728214666879

8086的地址总线是20位的,寻址能力1M,但是寄存器是16位,寻址能力64K,所以这里采用了将两个16位地址合成一个20位的物理地址的方法

地址加法器的原理:
物理地址=段地址16(对于16进制就是左移1位,二进制4位)+偏移地址

物理地址=CS*16+IP

1728214968000

注:上图都是16进制

//二进制每左移一位,这个数据(的十进制表示)*2。n位就是2^n。其他进制同理

段的概念

内存本身没有分段,分段实际上是CPU干的事,用于给出物理地址,这使得我们可以分段管理内存,偏移地址是16位,所以寻址能力为64K,所以一个段最大也就64K

//输出同一个物理地址,可以有许多组段地址和偏移地址

描述方法:数据在21F6H内存单元中=2000:1F60单元中=在2000段的1F60H单元中

段寄存器

8086(x86)有4个段寄存器

[CS](code segment,就是用来存储段地址的),[DS](data segment),[SS](stack segment),[ES](extra segment),x64会多4个,都是类似于ES的,不是专用

CS和IP是最常用的

整个过程:

1728218715382

注:IP有个指令自加器,会自动加上指令长度

CS与IP不能使用mov指令,要用jmp(转移地址)

jmp 2AE3:3   //也就是2AE30+00003=2AE33
JMP 3:0B16 //也就是00030+00B16=00B46,汇编指令不区分大小写

代码段

将长度为N(N<=64KB)的一组大麦,存在一组地址连续,起始地址为16的倍数的内存单元中,这就定义了一个代码段

只有CS:IP指定的代码段才会执行

Debug程序

1728220891479

R命令:这里实现了修改,这个可以修改CS和IP

1728220973614

T指令:会返回一个和R命令一样的查看(运行之后的)

D指令:返回的是十六进制

E命令:要使用机器指令的格式写操作

A命令:-a回车,接下来一条指令一执行

U命令:

1728221377761

退出输入quit(事实上q开头就行)

内存访问

内存中的存储

1728304242345

比如,上图中1地址单元存放的字型数据是124EH

俩个连续的内存单元,n和n+1,可以看成两个单元,也可以看成n字单元(不是n+1)的搞字节单元和低字节单元

DS(数据段寄存器)[address] //这是偏移地址

mov指令有两种转送功能:
1.将数据直接送入寄存器

2.将一个寄存器中的内容送入另一个寄存器

3.将一个内存单元中的内容送入一个寄存器

格式 :mov al,[0] //寄存器名,[内存单元地址(偏移地址)]

一个单元1字节,所以用al而不是ax,如果用ax,它会拉上它的高位组成一个字

此时的段地址就是DS里的地址,DS寄存器不能直接送数据,必须经过通用寄存器(这是硬件设计的缘故)

写入内存前先要确定好段地址:

1728305194634

mov,add,sub

MOV的所有格式:

1728305663779

现在,mov 寄存器,段寄存器

也是可以的

add/sub的所有格式:

1728305871479

数据段

用CS指向的是代码段,其他寄存器指向的都是数据段,定义同代码段

后进先出

CPU提供的栈机制

向下增长说的是从高地址(栈底),向低地址(栈顶)增长,此时制图是上低下高

为了更形象表示后进先出的特性,会倒着画,此时栈底是真的在地下,高地址

但是数据还是高地址读到低地址

CPU会提供相关指令来以栈的方式访问内存空间,将一段内存当作栈使用

push(入栈),pop(出栈)

push ax //将ax中的数据送入栈中(一字,对于8086)
pop ax //从栈顶取出数据放入ax(一字,对于8086)
高地址高位,低地址低位,在栈上就是高地址在下面

SS:存放栈顶的段寄存器 所有的段地址都不能直接写

SP:存放栈顶的偏移地址

任何时候:SS:SP指向栈顶

push操作会使SP=SP-2这样又指向栈顶(pop同理,但是是+)

push会先-2,靠近栈顶低地址,然后写入。

pop会先读出数据,然后+2,靠近栈底高地址。

空栈时SP指向最高地址单元的下一个单元(这个不在栈空间里)此时没有栈顶元素

(但是SS始终在栈的最高地址)

比如栈空间是10000H~10000FH

那么对应的SS是1000H,SP是0010H而不是000FH

栈顶超界问题

当满栈时继续push,或者空栈时继续pop,都会栈顶超界,写程序时不要超界

push,pop指令

在寄存器和内存之间传送数据,可以直接操作DS

push/pop [0] //可以用偏移地址,此时段地址是DS,不是SS,更不是CS

注意:

push/pop修改的只是SP,不会修改SS,所以栈顶只能是0~FFFFH,最大64K

用栈来暂存数据,出入栈顺序相反

比如:

...
mov ax,001AH
mov bx,001BH
push ax
push bx //bx先进
xor ax,ax
//这是最简便的清零方法,也可以用sub ax,ax或mov ax,0。区别是mov整个句字占3个字节,但是sub只要2个字节
xor bx,bx
pop bx //bx先出
pop ax
...

栈段

定义同数据段和代码段,<64K的连续内存空间

1728996769095

栈实际上是人为定义的内存空间,CPU不会认为pop,push的部分是栈(没有这个概念),也不知道空间是多大,你写到不是栈的地方也是可以的。(当然不能这么做,写的时候就要自己决定好大小不要写出去)

但是栈底要是再pop,会回到最低单元(栈顶,实际上严格来讲不能这么说)也就是说,SS不变,也就是只能在那64K的空间里环绕覆盖