赞
踩
想要学好汇编语言重要的是会用这个语言 编译 运行 操作,最好是需要用到的时候再去学,会发现自己很多疑惑的,这样的学习方式才是有意思的。不要别人学就跟着别人,学习之路是自己的,复制齐白石走过的路也不可能是齐白石。
CPU主搞运算的,你要是输入一条指令(instruction)就运行一次,然后停下来,等待下一条指令。指令都是二进制的,0和1,组合的时候可以代表很多意思,如果人去识别,那可难了。为了解决人类可读性的问题,以及偶尔的编辑需求人脑能快速看懂的知识,就诞生了汇编语言。
机器语言就是二进制的,0和1,机器才懂,加法指令是00000011
汇编语言的加法指令是ADD
高级语言的加法指令就是符号 +
学习汇编语言,最先看的是寄存器和内存模型。CPU 包括运算器和控制器还有寄存器,负责运算,不负责储存数据。数据一般都储存在内存之中,CPU 使用是去内存读写数据。但是,CPU 的运算速度远高于内存的读写速度,为了避免被拖慢,CPU 都自带一级缓存和二级缓存。基本上,CPU 缓存可以看作是读写速度较快的内存。
不同CPU,寄存器个数不一样,结构不一样。 拿8086CPU来说,14个
AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW
数据寄存器主要用来保存操作数和运算结果等信息,从而节省读取操作数所需占用总线和访问存储器的时间。
32位CPU有4个32位的通用寄存器EAX、EBX、ECX和EDX。对低16位数据的存取,不会影响高16位的数据。这些低16位寄存器分别命名为:AX、BX、CX和DX,它和先前的CPU中的寄存器相一致。4个16位寄存器又可分割成8个独立的8位寄存器:
AX可分为AH和AL.
BX可分为BH和BL.
CX可分为CH和CL.
DX可分为DH和DL.
每个寄存器都有自己的名称,可独立存取。程序员可利用数据寄存器的这种“可分可合”的特性,灵活地处理字/字节的信息。
每个可分为 ~H 与 ~L,如 AH 和 AL ,H 表示高八位,L 表示低8位。
上面图有点看不太懂啊? 原来是这样啊 如果 十进制数值2000 那么十六进制是4EH20H 而且高字节和低字节分开的十进制分别是 78 32
1个字节是8个比特 ,说明一个字节有8个0和1.
0x100是256个字节 ,那么0x100A就有4016个字节 ,增加22个字节 计算4038个字节是0x1020
寄存器AX通常称为累加器(Accumulator Register),用累加器进行的操作可能需要更少时间。累加器可用于乘、除、输入/输出等操作,它们的使用频率很高;
寄存器BX称为基址寄存器(Base Register)。它可作为存储器指针来使用;
寄存器CX称为计数寄存器(Count Register)。在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数;
寄存器DX称为数据寄存器(Data Register)。在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。
指针寄存器主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。指针寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。
寄存器BP称为基址指针寄存器(Base Pointer);
寄存器SP称为堆栈指针寄存器(Stack Pointer)。
变址寄存器主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式(在第3章有详细介绍),为以不同的地址形式访问存储单元提供方便。 变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。
寄存器SI称为源变址寄存器 (Source Index);
寄存器DI称为目的变址寄存器(Destination Index)。
内存模型:Heap (堆相关)地址从低到高
Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。
寄存器只能存放很少量的数据,大多数时候,CPU 要指挥寄存器,直接跟内存交换数据。所以,除了寄存器,还必须了解内存怎么储存数据。
程序运行的时候,操作系统会给它分配一段内存,用来储存程序和运行产生的数据。这段内存有起始地址和结束地址,比如从0x10000到0x90000,起始地址是较小的那个地址,结束地址是较大的那个地址。程序运行过程中,对于动态的内存占用请求(比如新建对象,或者使用malloc命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据)。
举例 如果起始地址0x1000开始分配,一直分配到地址0x100A,如果再要求得到32个字节,那么就分配到0x102A
内存模型:stack (栈) 地址从高到低
Stack 是由于函数运行而临时占用的内存区域。push 和 pop
所有的帧都存放在 Stack,由于帧是一层层叠加的,所以 Stack 叫做栈。生成新的帧,叫做"入栈",英文是 push;栈的回收叫做"出栈",英文是 pop。Stack 的特点就是,最晚入栈的帧最早出栈(因为最内层的函数调用,最先结束运行),这就叫做"后进先出"的数据结构。每一次函数执行结束,就自动释放一个帧,所有函数执行结束,整个 Stack 就都释放了。
Stack 是由内存区域的结束地址开始,从高位(地址)向低位(地址)分配。比如,内存区域的结束地址是0x1000,第一帧假定是16字节,那么下一次分配的地址就会从0xFF0开始;第二帧假定需要64字节,那么地址就会移动到0xFB0。
段寄存器有ES、CS、SS、DS、FS、GS、LDTR、TR共8个。
ES:扩展段。在串操作时(比如cmovs)目标操作数的基址是ES,源操作数是DS。
CS:代码段,配合EIP使用。
SS: 堆栈段,凡是基址是EBP或ESP的,段前缀就是SS。
DS:数据段,默认的都是DS。
FS、GS:80386 之后定义的。
读操作时,可以读到段寄存器的段选择子部分的16位。例如 mov ax,es 指令会把es寄存器的段选择子读到ax。
写操作时,会写入96位,其中源操作数的16位写入到段寄存器的段选择子部分,另外80位会根据段选择子从GDT表(全局描述表)中获取
段寄存器的大小是 96 位
段寄存器结构可以抽象成以下结构
struct Segment
{
WORD Selector; //16位段选择子,可见部分. 使用OD 或者X64dbg看段寄存器只会显示16位的段选择子可见部分.当读段寄存器(如mov ax,CS)的时候,只会返回这16位。或者push seg 操作针对的都是这16位。如果目标操作数是32位(如mov eax ,CS),则将16位零扩展成32位赋给目标操作数。但是写的时候,就会涉及到96位
WORD Attribute; //16位表示的段属性, 表示了当前段寄存器是可读的可写的还是可执行的
DWORD Base; //32位表示的基址,表示段从哪里开始
DWORD limit; //32位表示,表示的是基址的长度. base + limit 可以确定一个段的大小
}
段地址x16+偏移地址=物理地址 常用说法左移4位。位数指二进制位 就是原有的基础上面二进制的机器语言上面左移4位 ==就是2的4次方
下面这个图的思考很重要的,又要长脑袋了
那么如何实现1000H送入ds段寄存器呢? 可以用一个通用寄存器中转。下面的问题就是这个情况
问题3.2
写几条指令,将al 中的数据送入内存单元 10000H 中?思考后看分析。
分析:
怎样将数据从寄存器送入内存单元?从内存单元到寄存器的格式是:“mov 寄存器
名,内存单元地址”,从寄存器到内存单元则是:“mov 内存单元地址,寄存器名”。
10000H 可表示为 1000:0,用 ds 存放段地址 1000H,偏移地址是 0,则 mov [0],al 可完成
从al到 10000H 的数据传送。完整的几条指令是:
mov bx,1000H
mov ds,bx
mov [0],al
相关的jmp知识:CPU=CSx16+IP 就是CS向后移动一位再相加。
mov:数据传送
示例:mov ax,bx
说明:将bx中的数据传送到ax中,因为使用的是ax和bx,所以数据的长度是16位,下面的例子都相同,如果有操作数据中有寄存器,那么按照寄存器的数据长度计算
push:入栈
示例:push ax
解释:将ax的数据入栈,传送到ss:sp栈顶处
pop:出栈
示例:pop ax
解释:将ss:sp位置的数据出栈,传送入ax中
pushf:所有标志位入栈
示例:pushf
解释:标志位入栈,防止后面的操作对标志位产生影响的通常做法
popf:标志位出栈
示例:popf
解释:标志位出栈,用于还原入栈的标志位
xchg:交换,目前没用到
add:加法
示例:add ax,2
解释:将ax中的数据加2,即ax+0002h
sub:减法
示例:sub ax,2
解释:将ax中的数据减2,即ax-0002h
adc:加法(带符号位)
示例:adc ax,2 ;CF=1
解释:将ax中的数据加3,即ax+0002h+1h
sbb:减法(带符号位)
示例:sbb ax,2 ;CF=1
解释:将ax中的数据减3,即ax-0002h-1h
inc:自增
示例:inc si
解释:将si中的数据加1,常用在循环或条件转移中
dec:自减
示例:dec ax
解释:将ax中的数据减1
cmp:比较
示例:cmp ax,0
解释:相当于减法,ax-0,只不过不影响寄存器的值,而只影响标志寄存器,因为条件转移指令是依据标志寄存器的指令,所以cmp常与条件转移指令配合使用进行条件转移
mul:乘法
示例:mul bx或mul bl
解释:分为两种情况:
1. 指令参数是8位寄存器如bl时,乘数1默认放在al寄存器中,另一个乘数2放在8位寄存器如bl中,结果存在ax中
2. 指令参数是16位寄存器如bx时,乘数1默认放在ax寄存器中,另一个乘数2是指定的16位寄存器如bx中的数据,结果的高16位存在dx,低16位存在ax中
div:除法
示例:div bx或div bl
解释:同样分为两种情况:
1. 指令参数是8位寄存器时,被除数(除法前面那个数。。。)则为16位,默认存放于ax中,除数则是存放于指定8位寄存器如bl中,结果为:al存储商,ah存储余数
2. 指令参数是16位寄存器时,被除数则为32位,默认存放于ax和dx中,dx存高16位,ax存低16位,除数存放于指定16位寄存器如bx中,结果:ax存储商,dx存储余数
aaa:目前没有用到
or:逻辑或
示例:or al,00100000
解释:示例中结果是将al中第6位置为1,其他位保持不变,同样也能实现将大写字母转化为小写字母的简化运算,需要将字母与00100000做逻辑或运算即可实现
not:逻辑非,不常用
示例:
解释:
xor:逻辑异或,目前不常用
示例:
解释:
test:不常用
shl:逻辑左移
示例:shl al,1或mov cl,3;shl al,cl
解释:逻辑左移的意思就是左移后,移出的数据存放在标志寄存器CF中,而最低位用0补齐,也分为两种情况:
1. 左移1位:直接shl al,1即可
2. 左移超过1位:需要先将欲移动的位数据存入cl中,再通过左移cl个位的数据来实现
shr:逻辑右移
示例:shr al,1或move cl,2;shr al,cl
解释:逻辑右移与逻辑左移类似,这里就不多讲了,同样也是两种情
jcxz:如果cx寄存器的值为0,则转移到指定标号
示例:mov cx,0;jcxz s
解释:如果条件满足cx寄存器的值为0,则转移到指定的标号处,常用的场景是:遍历一个以'0'字符结尾的字符串,根据这个0判断字符串是否到末尾的简单实现方式
je:如果cmp得差结果等于0,则转移到指定标号
示例:mov bx,3;cmp bx,3;je s
解释:将会转移到s处
jb:如果cmp的差结果小于0,则转移到指定标号
示例:mov bx,3;cmp bx,4;jb s
解释:将会转移到s处
ja:如果cmp的差结果大于0,则转移到指定标号
示例:mov bx,3;cmp bx,2;ja s
解释:将会转移到s处
jnb:如果cmp的差结果不小于0,则转移到指定标号
示例:mov bx,3;cmp bx,3;jnb s
解释:将会转移到s处
jna:如果cmp的差的记过不大于0,则转移到指定标号
示例:mov bx,3;cmp bx,3;jna s
解释:将会转移到s处
loop:汇编语言中的循环语句
示例:mov cx,10;loop s
解释:将s程序段循环执行10次(循环次数由cx的值指定)
过程
call:调用子程序,常与ret成对使用
示例:call s;s:ret
解释:转移到子程序,类似于转移指令,但相当于执行了
push IP
jmp near s这两条指令,记录了转移的位置,可以使用ret返回此IP的位置
ret:在子程序中同于返回call的指令处,常与call成对使用,并且是近转移
示例:call s;s:ret
解释:从子程序跳出,相当于执行了
pop IP,程序执行的下一条语句就是原来call的IP的地址,从而实现了近转移
retf:在子程序中同于返回call的指令处,常与call成对使用,并且是远转移
示例:call s;s:retf
解释:从子程序跳出,相当于执行了
pop IP
pop CS程序执行的下一条语句就是原来call的IP的地址,从而实现了远转移
std:这是DF为1(即反向拷贝)
示例:std;rep movsb
解释:设置标志寄存器DF为1,即设置拷贝的方向为反向
cli:设置TF标志位为1
示例:cli
解释:设置TF为1后,当接受到可屏蔽中断时,会在下一条指令执行中断
sti:设置TF标志位为0
示例:sti
解释:设置TF为0后,当接受到可屏蔽中断时,会忽略中断继续执行当前程序直至结束
nop:添加一个占位的一字节数据
示例:funcend:nop
解释:常用来记录子程序的段结束的位置,比如offset funcend就可以获取func结束位置的偏移地址
ZF:是否为0
PF:1的个数是否为偶数
SF:是否为负数
有符号整数,只看结果是不是大于7,也就是首位是不是0
CF:是否存在进位或借位(无符号整数)
直接进行无符号运算,首位进位就置1
OF:是否存在溢出(有符号整数)
16位cpu,有符号溢出范围79H(127),-80H(128)
DF:方向标志位,与movsb配合使用
学习完基础知识就应该做操作了
操作开始第一步就是安装好环境 由于我使用的是新版Windows11 没有了debug的内部使用
需要另行安装,所以需要官网下载DOSBox,然后下载插件debug
实验1:查看CPU和内存 观察机器指令和汇编指令编译过程
《汇编语言第三版》王爽著,还有csdn上面各个大神的总结
段寄存器参考为CSDN博主「maomao171314」的原创文章
原文链接:https://blog.csdn.net/maomao171314
通用寄存器参考CSDN博主「幽_篁」的原创文章,
原文链接:https://blog.csdn.net/hallyz945
汇编指令知识参考CSDN博主「coderqjinx」的文章
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。