一、计算机体系
总线分为:地址总线,数据总线,控制总线
地址总线宽度 | 数据总线宽度 | |
8086 | 20(寻址空间2^20) | 16 |
80286 | 24 | 16 |
80386 | 32 | 32 |
在16位cpu体系:
物理地址=基础地址+偏移地址=段地址*16+偏移地址
段基础地址=段地址*16
偏移地址=16位=2^16=[0000, FFFF]
段寄存器:用于保存段地址,CS,DS,SS,ES
IP:指令指针寄存器=偏移地址
在8086机下,物理地址=指令地址=CS*16+IP
x86通用寄存器:eax,ebx,ecx,edx,esi,edi,ebp,esp。e代表extend,扩展到32位
二、指令寄存器cs、ip
修改cs,ip寄存器只能用,jmp指令,不能用mov指令,格式:jmp 段地址:偏移地址,段地址用于cs,偏移地址用于ip。
若只修改ip的内容,可以通过 "jmp 某一合法寄存器",例如: jmp ax,jmp bx
三、数据寄存器ds & bx
在8086下,物理地址=段地址*16+偏移量,数据的段地址保存在ds寄存器中,bx保存偏移地址
比如访问物理地址10000H,应该执行如下命令
mov bx, 1000H
mov ds, bx
mov al, [0]
说明:
1) 物理地址10000H = 1000H*16 + 0 ==> 段地址是1000H,偏移地址是0
2) ds是段寄存器,必须通过一个通用寄存器周转一下才可以,写入1000H
3) [0],表示偏移量,mov al [0] 会默认从ds寄存器中取出段地址,因此在执行此步骤之前必须讲ds寄存器设置正确
如果寄存器值写回内存地址,应该是mov [0] al
三、栈寄存器ss sp
ss:栈基址,栈底地址=ss*16
sp:偏移量,栈顶
在一个栈空间,ss是不变的,只有sp在改变。入栈,sp是从高地址向低地址改变。
【问题】
如果将10000H~1FFFFH这段空间当作栈段,初始状态栈是空的,此时,SS = 1000H, SP = ?
这个题非常好,说一下我解题思路。
1)首先入栈是从高地址向低地址改变(反之,是出栈)。也就是说栈基址就是10000H,那么用栈寄存器保存就应该是1000H (1000H*16 = 10000H),所以ss=1000H,是没有问题的。那么sp是多少呢?
2)题目中1FFFFH是边界,而且我们知道栈操作是按照字为单位的(也就是两个字节),那么第一个入栈元素占用的空间地址就是 1FFFEH 1FFFFH,如下图所示:
也就是说SP指向1FFFEH时栈中有一个元素,那么当栈中没有元素,应该指向20000H。但是,SP寄存器是16位的,只能保存0000,所以这道题SP=0
引伸题:当栈空的时候sp=0,当栈满的时候sp=?
我们知道sp和ss指向同一个地址的时候代表栈满,所以根据公式,物理地址=SS*16+SP=10000H 且SS=1000H,最终SP=0。也就是说当栈满的时候sp=0。那么问题就来了,sp=0的时候,是栈空还是栈满呢?这个需要根据上下文才能确定,sp指向的实际地址,或者说需要程序员来保证。
四、中断
中断分为内中断和外中断
内中断:是由cpu内部抛出来的中断,例如除以0,int指令
外中断:是外部设备(比如网卡)发出的中断
发生中断后,需要将当前的标志寄存器、cs、ip压入栈中进行保存,然后根据中断码,从中断向量表中获取,中断处理程序的入口,即重新设置cs,ip
在8086cpu机器下,中断向量表起始地址是0000:0000 ~ 0000:03FF(256个),一个中断向量表中的一个表项大小是两个字(4个字节),高地址字存放的段地址,低地址字存放的偏移地址,所以cs = 4*N+2,ip = 4*N
我们单步追踪程序,是由cpu提供了单步中断功能实现的(本质是,标志寄存器TF标志控制)
五、寄存器列表
寄存器 | 解释 | 备注 | |
通用寄存器 | AH&AL=AX(accumulator) | 累加寄存器 | |
BH&BL=BX(base) | 基址寄存器 | 用于寻址 | |
CH&CL=CX(count) | 计数寄存器 | ||
DH&DL=DX(data) | 数据寄存器 | ||
DI | 目的变址寄存器 | bi,si的功能和bx类似,但是bi,si不能拆成8位寄存器 | |
SI | 源变址寄存器 | ||
段寄存器 | CS(Code Segment) | 代码段寄存器(指令寄存器) | |
DS(Data Segment) | 数据段寄存器 | ||
SS(Stack Segment) | 堆栈段寄存器 | ||
ES | 附加段寄存器 | ||
地址偏移 寄存器 | SP(Stack Pointer) | 堆栈指针寄存器,与SS配合 | |
BP(Base Pointer) | 基址指针寄存器 | ||
IP(Instruction Pointer) | 指令指针寄存器,与CS配合 |
六、常用指令
指令 | 形式 | 举例 |
mov | mov 寄存器, 数据 | mov ax 0123H ==> ax = 0123H |
mov 寄存器, 寄存器 | ||
mov 寄存器, 内存单元 | 该形式使用的时候需要注意: 在window debug模式下,是可以直接 mov ax [x] 但是在编码的时候不可以,因为编译器会把[x],当成数字x使用 在编码的时候需要,有两种方式: 1)将地址数字先存到寄存器中, mov bx 1000H mov ax [bx] 2)在地址前面写上ds mov ax ds:[x] | |
mov 内存单元, 寄存器 | ||
mov 段寄存器, 寄存器 | ||
mov 寄存器, 段寄存器 | ||
mov 内存单元, 段寄存器 | ||
mov 段寄存器, 内存单元 | ||
mov 寄存器, [bx] | 段地址在ds中, 偏移地址在bx中,将这个内存单元存储到ax中 | |
mov word ptr | ||
lea | 取源操作数地址的偏移量,并把它传送到目的操作数所在的单元 | SI=1000H , DS=5000H, (51000H)=1234H |
add 加法指令 | add 寄存器, 数据 | |
add 寄存器, 寄存器 | add ax, bx ==> ax=ax+bx | |
add 寄存器, 内存单元 | ||
add 内存单元, 寄存器 | ||
sub 减法指令 | sub 寄存器, 数据 | |
sub 寄存器, 寄存器 | sub ax, bx ==> ax=ax-bx | |
sub 寄存器, 内存单元 | ||
sub 内存单元, 寄存器 | ||
div 除法指令 | div reg div 内存单元 | 除数: 有8位和16位两种,在一个寄存器或内存单元中. |
被除数: 默认放在AX和DX或AX中 除数为8位, 被除数为16位, 默认在AX中存放. 除数为16位, 被除数为32位, 在DX或AX中存放. AX存放低16位,DX存放高16位. | ||
结果 除数为8位, 则AL存储除法操作的商, AH存放余数 除数为16为, 则AX存储除法操作的商, DX存放余数 | ||
mul乘法指令 | mul reg mul 内存单元 | 数据:两个相乘数,要么都是8位,要么都是16位。 如果是8位,一个数字默认存放在al中,另外一个数字存放在其他8位寄存器中或者字节型内存单元中。 如果是16位,一个数字默认存放在ax中,另外一个数字存放在其他16位寄存器中或者字型内存单元中。 结果:8位乘法,得到一个16位数, 结果存放在ax中 16位乘法,得到一个32位数, 低16位存放在ax中,高16位存放在dx中 |
push 只能用于字操作,不可以操作字节 | push 寄存器 | |
push 段寄存器 | ||
push 内存单元 | 将某个内存单元,压入栈, | |
pop 只能用于字操作,不可以操作字节 | pop 寄存器 | |
pop 段寄存器 | ||
pop 内存单元 | 出栈,将数据写入内存单元 | |
loop | 循环指令,需要结合cx寄存器一起使用 | |
inc | inc 寄存器 | 用于自增 |
and | 逻辑与 | |
or | 逻辑或 | |
dup | 伪指令,用于分配内存 | db 3 dup (0) 等价 db 0, 0, 0 |
jmp | 可只修改ip,或者修改ip和cs | jmp short 标号 (短转移,-128~127)根据当前ip所在位置进行偏移,即ip = ip + 8bit位移 |
jmp near ptr 标号 段内近转移, ip = ip + 16bit位移 | ||
jmp far ptr 标号 段间远转移,可同时修改cs和ip两种寄存器(段间转移,高地址处的字是目的段地址,低地址处的字是偏移地址) 例如:机器码=EA0B01BD0B 可得 段地址=0BBD 偏移地址=010B | ||
jmp 16位寄存器, 转移地址在16位寄存器中,等效于ip = 寄存器值 | ||
jmp word ptr 内存单元地址 (段内转移) | ||
jmp dword ptr 内存单元地址 (段间转移,高地址处的字是目的段地址,低地址处的字是偏移地址) | ||
jcxz | 条件转移,段内短转移 | jcxz 标号 (偏移 -128 ~ 127) 含义:当寄存器cx等于0,跳转,反之不跳转 与loop指令正好相反 |
call | 调用子程序,将ip 或 cs、ip入栈 | call 标号 (将ip入栈) |
call far ptr 标号(先cs入栈、再ip入栈) | ||
call 寄存器(16bit,将ip入栈) | ||
call word ptr 内存单元地址(将ip入栈) | ||
call dword ptr 内存单元地址(先cs入栈、再ip入栈) | ||
ret | ret 用栈中数据修改ip,实现短转移 | ret指令,等价 (ip) =((ss)*16 +(sp)) (sp) = (sp) + 2 |
retf | retf用栈中数据修改cs、ip实现远转移 | retf指令,等价 (ip) =((ss)*16 +(sp)) (sp) = (sp) + 2 (cs) = ((ss) * 16 + (sp)) (sp) = (sp) + 2 |
iret | 中断处理程序中必须使用此指令进行返回 | 该指令等价于: pop ip pop cs popf |
db | 定义一个字节 | |
dw | 定义一个字(2字节) | |
dd | 定义一个双字(4字节) | |
movsb | 将ds:si指向的内存单元中字节送入es:di,然后si、di根据标志寄存器DF表示,进行递增、递减 | DF=0 si、di递增1 DF=1 si、di递减1 |
movsw | 将ds:si指向的内存单元中字送入es:di,然后si、di根据标志寄存器DF表示,进行递增2、递减2 | DF=0 si、di递增2 DF=1 si、di递减2 |
rep movsb rep movsw | 通过cx控制循环次数 | rep movsb 等价 s: movsb loop s |
cld | 将标志寄存器df设置为0 | |
std | 将标志寄存器df设置为1 | |
pushf popf | 将标志寄存器压/出栈 | |
in | 从端口读数据 | in al, 60h,从端口编号60中读取数据,并保存在al中 |
out | 将数据写入端口 | out 20h, al 注:in out只能利用ax,al寄存器 |
shl、shr | 左移,右移操作 移出的数据保存在CF中 | mov al, 11001000b shl al, 1 左移1位, al = 10010000b CF=1 |
注意:add ds, ax 不可以
寻址方式,如下图所示:
AT&T汇编风格:《GCC汇编器语法》
《64位寄存器-寄存器名称与数据类型》