寄存器
一、分类
- 通用寄存器:AX,BX,CX,DX
- 段寄存器:CS,SS,DS,ES
- 其他(待补充):SP,IP
- CS and IP作指令指针,SS and SP作栈顶指针,DS作默认段地址
- 这些寄存器都是16位的
在x86系统中全面升级为32位 【Extend】
并有了新的名字, 比如EAX这些都是32位的, 也可以使用AX作为16位 AL、AH8位
二、CS和IP
CS为代码段寄存器,IP为指令指针寄存器,CPU工作方
INSTRUCTION = CS*16+IP
想要改变CS、IP的值,不能用mov,需要用jmp指令(功能上是相同的):
jmp IP,ax == mov IP,ax
三、Debug调试工具功能
1. -r: 查看、改变寄存器的内容 |
四、栈
- SS存放栈顶的段地址,SP存放偏移地址,即SS:SP指向栈顶元素,栈空时,SS指向最高位,SP指向最低位。
- 注意1000-100F为十六个字节,定义栈空间大小后,CPU不会自动阻止超界,注意栈空间的管理。
- push R/SR/Unit ;入栈
pop R/SR/Unit ;出栈 - mov ax,2000
mov ss,ax
mov sp,10 ;安排2000:0000~2000:000F为栈空间,初始化栈顶
程序基础
一、知识点
- 源程序中包含两种指令,一种
汇编指令
,一种是伪指令
,前者有机器码对应,由CPU执行;后者没有机器码,仅通过编译器执行。 - 伪指令
segment 和 ends:一个段的开始和结束。
end: 区别于ends,end表示汇编程序的结束(ends可理解为end segment)。
assume: 可将段与寄存器关联起来,如:assume cs: codesg
对应将codesg段与cs寄存器关联
。 - 程序加载后,DS中存放程序所在内存区的段地址,PSP(通信区占256个字节)为占以为(指令位置一个单元为1kb),如ds=075A,则
段地址sa*16+偏移地址ea=cs地址
,cs=076A - loop指令实现循环功能,cx存放循环次数。cx==0才向下执行。此方式每次都先执行循环内容,在loop语句cx减1
用法实例:
mov cx,12 |
用masm编写程序debug到最后一部int 21h,需要执行p选项。
字母开头的数字前面要添零,例:9138H
、0A000H
debug中p命令能快速执行完Loop语句
一般情况 0:200~0:2ff
为一段安全的空间
ds:[bx]
中ds
为段前缀
段前缀的使用,操作将一组连续的数据从0ffffh转到0200h
运用两个寄存器ds和es,从ds录入,从es输出
汇编lea
指令:load effective address, 加载有效地址,可以将有效地址传送到指定的的寄存器。指令形式是从存储器读数据到寄存器, 效果是将存储器的有效地址写入到目的操作数, 简单说, 就是C语言中的”&”
byte是字节,也就是8位。用来储存char或者char类型指针。
word是字,也就是16位。用来储存16位整数或者16位地址。
dword是双字,也就是32位。可以用来储存32位整数或者32位内存地址。
在汇编中使用和占用的空间和寄存器大小不同。
二、编译器和汇编语法差异
mov ax,2000h |
首先看看:状态寄存器(即标志寄存器)
PSW(Program Flag)程序状态字(即标志)寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成,
如下所示:
条件码:
①OF(Overflow Flag)溢出标志,溢出时为1,否则置0.标明一个溢出了的计算,如:结构和目标不匹配.
②SF(Sign Flag)符号标志,结果为负时置1,否则置0.
③ZF(Zero Flag)零标志,运算结果为0时置1,否则置0.
④CF(Carry Flag)进位标志,进位时置1,否则置0.注意:Carry标志中存放计算后最右的位.
⑤AF(Auxiliary carry Flag)辅助进位标志,记录运算时第3位(半个字节)产生的进位置。
有进位时1,否则置0.
⑥PF(Parity Flag)奇偶标志.结果操作数中1的个数为偶数时置1,否则置0.
控制标志位:
⑦DF(Direction Flag)方向标志,在串处理指令中控制信息的方向。
⑧IF(Interrupt Flag)中断标志。
⑨TF(Trap Flag)陷井标志。
test属于逻辑运算指令
功能: 执行BIT与BIT之间的逻辑运算
测试(两操作数作与运算,仅修改标志位,不回送结果).
Test对两个参数(目标,源)执行AND(二进制计算)逻辑操作,并根据结果设置标志寄存器,结果本身不会保存。TEST AX,BX 与 AND AX,BX 命令有相同效果
语法: TEST r/m,r/m/data
影响标志: C,O,P,Z,S(其中C与O两个标志会被设为0)
CMP属于算术运算指令
功能: 比较两个值(寄存器,内存,直接数值)
语法: CMP r/m,r/m/data
标志位: C,P,A,Z,O
CMP比较.(两操作数作减法,仅修改标志位,不回送结果).
cmp实际上是只设置标志不保存结构的减法,并设置Z-flag(零标志).
零标志很像carry,也是内部标志寄存器的一位.
结论:
test逻辑与运算结果为零,就把ZF(零标志)置1;
cmp 算术减法运算结果为零,就把ZF(零标志)置1.
包含多个段的程序
dw(define word)
定义字型数据, 以两个字节为单位的一组数据
放在CS指向的一段连续的内存空间
也就是在程序的开始处安排数据区 之后才是代码区
这样编译程序会出现入口不对的问题, 于是多段程序诞生了
样式
assume cs:code |
数据、代码、栈放在不同的段
一个段的容量不能大于64KB,这是8086的限制
所以写程序的时候为了安全 将数据和代码还有栈分在不同空间是一种美德
下面示范
assume cs:code,ds:data,ss:stack |
细节说明
- 对于段地址的引用
;正规操作
mov ax,data
mov ds,ax
mov bx,ds:[6]
;错误示范
mov ds,data
mov bx,ds:[6]
;8086cpu不允许将一个数指直接送入段寄存器中!!
各个段都是我们自己的安排
名称当然也是自己定的
灵活定位内存地址
[BX+idata+(SI)+(DI)]
处理内存空间连续但不同的字符串的时候,经常会用到这种方式来定位下一个字符串
SI 和 DI寄存器 可以辅助来进行定位(SI DI 二选一)
idata象征常量
其他寄存器主要存储变量, 当然常量也可以, 不过没必要
两重循环
cx寄存器只有一个
父级的循环计数器的值需要存放在栈里, 回到循环之前出栈使用
新姿势
- 对于字母的大小写转换, 小写和大写之间在二进制有第五位为1或0的偏差
利用and/or 置0/1 可以快速实现大小写的转换
C试例如下:
a[i] = a[i] & 0xDF //置第五位为0 转换为小写
b[i] = b[i] | 0x20 //置第五位为1 转换为大写
数据处理
bx, si, di, bp
只有这四个寄存器能用于[…] 单位寻址
组合的时候 bx bp 不能同时出现 si di 同上
使用bp的时候 默认的段地址在SS(BX默认的段地址在ds)
div
整数除法 默认被除数大于除数 除数为8为时被除数16位, 除数视为16位时, 被除数视为32位
被除数: 默认存放在ax/dx中, 16位时ax中, 32位时ax低16, bx高16
除数: 跟在div指令后面 8 位或 16位
结果: 16位模式下 al存放商 ah存放余数
32位模式下 ax存放商, dx存放余数
mul
整数乘法 mul reg/mem
操作数一个在al(8)/ax(16) 一个在reg/mem 中
结果存放在ax(16) 或者 低位ax 高位dx(32) 中
db dw dd 配合 dup
d代表define
db -> define byte ;8位
dw -> define word ;16位
dd -> define double word ;32位
dup配合这三种数据使用, 代表着数据的重复, 示例如下:
db 3 dup (0) ;等价于db 0,0,0 |
常见于初始化大片区域为0
转移指令
定义: 修改IP (段内转移)或者 同时修改 CS 和 IP(段间转移) 的值的指令
分类
- 无条件转移指令
- 条件转移指令
- 循环指令
- 过程
- 中断
按照位移的转移
段内转移
短转移
jmp short xxxx
对应机器码 EB XX
, 转移长度为8位
跳跃范围为当前地址的-128~127
采取的方式为相对寻址
机器码EB后面的数 XX = 目的地址 - 下一条指令地址
近转移
jmp near xxxx
原理同上, 转移长度为16位
JCXZ
等价于 if(cx == 0) -> jmp short xxxx
LOOP
常用
按照地址的转移
远转移, 实现的是段间转移
jmp far ptr xxxx
jmp far ptr s ;EA 0B 01 BD 0B |
Call 和 RET
都是转移指令
ret 和 retf
都是利用栈种的数据
ret 修改 IP, 实现近转移,实质如下:
pop IP |
retf 修改CS:IP, 实现远转移,实质如下:
pop IP |
Call指令
操作分为两步
- 将下一条地址的位置入栈
- 跳转
不能实现短转移, 至少为16位的近转移
call + 标号
等价于
push ip |
call far ptr + 标号
等价于
push cs |
内中断
产生方式大致有四种:
- 除法溢出 对应中断类型码0
- 单步执行 对应中断类型码1
- into指令 对应中断类型码4
- int x 对应中断类型码 x
开始时
先入栈EFLAGS寄存器
然后入栈CS 和 IP
中断例程的最后会用到iret指令
相当于
pop IP |
端口
访问端口与通常访问寄存器的方式不同
通常的mov, lea等访问指令都不能用来访问端口
访问端口仅有两条指令
in
用于读入数据到寄存器
out
用于将数据写入端口
CMOS RAM
该RAM芯片提供两个端口
分别为70h
和71h
70h
为 地址端口
71h
为数据端口
通过这两个端口可以实现数据的读写
- 将2送入端口70h
- 从71h可以读取到2号单元的内容
外中断
PC系统中,外中断源分为两类
- 可屏蔽中断
多种中断类型码 - 不可屏蔽中断
中断类型固定为2
键盘处理
键盘输入 产生扫描码
送入60h端口引发可屏蔽中断 中断类型码为9