1.汇编语言发展至今,由以下3类指令组成
汇编指令:有对应的机器码
伪指令:没有机器码,由编译器执行
其他符号:没有机器码,由编译器识别
2.在内存或磁盘中,指令和数据没有任何区别,都是二进制信息
3.CPU是可以执行机器指令的微处理器,要想让它工作,就必须向它提供指令和数据,即存储器(内外存)
它们要交互:
1)存储单元的地址(地址、从哪起始)
2)读或写命令(控制信息)
3)读或写的数据(数据)
这三者当然是通过电信号传递,也即导线,连接CPU和其他芯片的导线,通常称为总线:即以上三类(地址、控制、数据)总线(外部总线)
4.存储器的容量是以字节为最小单位计算的,所有的物理存储器都被看作一个由若干存储单元组成的逻辑存储器,此大小受地址总线限制,8086为2~20次方即1M
80386为2~32次方,即4G
5.CPU由运算器、控制器、寄存器等器件构成(说明下,存储器和寄存器名字相似,这两者可是相差千万),内部总线连接它们,进行数据传送
ax,bx,cx,dx为通用寄存器,为了向上兼容,又可以分为h+l两类8字节的,如ax可以分为ah+al
以下为基础赋值
mov ax,18 |
mov ah,78 |
add ax,8 |
mov ax,bx |
add ax,bx |
6.如
mov ah,0 |
mov al,c5H |
add al,93H |
7.所有内存单元构成的存储空间,每个内存单元在这空间中都有唯一的地址(物理地址),CPU通过地址总线送入存储器,必须是一个物理地址,所以在它发出这个地址之前,它要先在内部形成这个物理地址,不同的CPU有不同的形成方式
8086有20位地址总线,也就是存储空间可以是2~20次方,而它的寄存器只有16位,所以必须2个一起组合(段地址+偏移地址)
地址加法器会把它们转换成20位物理地址,物理地址=段地址*16+偏移地址
8.段地址可以在编程中视为起始地址,因为段地址*16必然是16的部数,所以段超始地址也一定是16的倍数,偏移地址为16位,所以最大长度为64K
CPU可以通过不同的段地下+偏移地址形成同一个物理地址
CS、DS、SS、ES为段寄存器
9.CS:IP执行指令的过程是:
1)地址加法器转换CS*16+IP为物理地址,通过总线,找到外部的这个物理地址,读取指令进入指令缓冲区
2)IP=IP+所读取指令的长度,从而指向下一条指令
3)执行指令,转到步骤1)
所以CPU就是通过CS:IP来把内存单元的内容看成指令的,不然指令和数据没有任何区别
10.mov是不能改变CS、IP的值,能够改变它们的指令统称为转移指令,如jmp
jmp 2AE3:3 | 执行后,CS=2AE3,IP=003 |
jmp ax | 执行后,CS不变,IP=ax |
11.win7-64模拟debug:下载地址
1.安装dosbox,运行,debug.exe放在d盘
2.输入命令mount c d:\
当出现Drive C is mounted as local directory d:\的时候,成功
"c"是作为虚拟c盘的意思,"d:\"是虚拟文件夹的位置(debug.exe)
3.输入c:
4.输入debug
u查看汇编,a编缉汇编 ,windbg估计就是抄它的吧
先q:退出,再cls清屏
12.8086不支持把数据直接送到段寄存器中,即不能mov ds, 1000h
所以这样写:
mov bx,1000h |
mov ds,bx |
这其实也是debug和masm对指令的不同处理,目前的解决方式就是都是先赋到寄存器中,再mov eax,[ebx]这样(112page)
mov 寄存器,数据 | mov ax,8 |
mov 寄存器,寄存器 | mov ax, bx |
mov 寄存器,内存单元 | mov ax,[0] |
mov 内存单元,寄存器 | mov [0],ax |
mov 段寄存器,寄存器 | mov ds,ax |
13.栈
栈增加是从地址高到地址低增长的,任意时刻,SS:SP都指向栈顶元素,当栈空时,即不存在栈顶元素,所以SS:SP只能指向栈最底部单元下面的单元
push ax:
1).SP=SP-2
2).ax送入SP所在位置
pop ax
1)将SP所在位置的值送入ax中(注意栈是从高向低的)
2)SP=SP+2
14.栈溢出
栈满再执行push,就会覆盖到栈顶以上的数据(即比栈顶还小的地址被覆盖)
栈空再执行pop,会pop栈底以下的数据
由于SP是16位的,所以如果栈底是0XFFFE时,再POP就变成0了,再POP就变成2了
15.loop指令
1).cx = cx-1
2).判断cx中的值,不为0,则转到标号执行
16.伪指令assume cs:code,ds:data,ss:stack,这样做后,CPU是不会把csr指向code,ds指向data的,这就是一个标识,仅此而已
所以还是要自己在代码中传递
17.可以用' '单引号把字符串转换成对应的ASII码,一个字符对应一个字节转换,所以
__asm
{
push eax
mov eax, 'cdef'
pop eax
}
这样是正确的,eax支持四个字节,所以1到4个字节都是OK的,多了就会报错
18.[bx+idata]表示内存单元
char *g_char = "123456";
int main()
{
__asm
{
push eax
mov ebx, g_char
mov eax, [ebx+1]
pop eax
}
像mov eax, [ebx+ecx+1]也是OK的
19.在没有寄存器名存在的情况下,用操作符X PTR指明内存单元的长度,X可以为DWORD,WORD BYTE等
20.可以修改CS:IP的相关指令统称为转移指令
1>.无条件转移指令(jmp)
2>条件转移指令
3>循环指令(loop)
4>过程
5>中断
offset用于取得标号的偏移地址:在386中取得标号在程序中的地址
如:
char *g_char = "123456";
int main()
{
__asm
{
push eax
s:
mov eax, offset s
s1:
mov eax, offset s1
pop eax
}
编译后反汇编是如下的:
20.jcxz s如果cx=0,则转到s,否则无视它
21.call 对当前下一条指令的IP压栈,再跳转 ret对IP出栈
22.标志寄存器(一般运算指令会影响,如add、sub、mul、div、inc、or、and,而传送执行是没有影响的,如mov、push、pop)
ZF:结果为0,zf=1
PF:1的个数为偶数,pf=1
SF:负数时sf=1
DF:为0,循环操作时,si、di递增,否则递减
溢出标志OF(Over flow flag) OV(1) NV(0)
方向标志DF(Direction flag) DN(1) UP(0)
中断标志IF(Interrupt flag) EI(1) DI(0)
符号标志SF(Sign flag) NG(1) PL(0)
零标志ZF(Zero flag) ZR(1) NZ(0)
辅助标志AF(Auxiliary carry flag) AC(1) NA(0)
奇偶标志PF(Parity flag) PE(1) PO(0)
进位标志CF(Carry flag) CY(1) NC(0)
CF: 无符号数运算时记录进位值或借位值,有则为1
mov al,98h
add al,al //al=30h,cf=1,进位
mov al,97h
sub al,98h//al=ffh,cf=1,借位了
OF:有符号数溢出则为1
如前面add al,al,就会有溢出of=1
adc ax,bx 等于 ax = ax+bx + cf(0或1)
所以
add ax,bx
=
add al,bl
adc ah,bh
sbb相反
23.cmp指令相当于减法指令,只是不保存结果,但影响标志寄存器
cmp ax, bx的逻辑含义:
zf=1 说明 ax = bx
zf=0 说明 ax !=bx
cf=1 说明 ax < bx
cf=0 说明 ax >= bx
cf=0&&zf=0说明ax>bx
cf=1或zf=1,说明ax<=bx
24.条件转移指令
1>无符号比较:
je 等于则转移 zf =1
jne 不等于则转移 zf = 0
jb 低于则转移 cf = 1
jnb 不低于则转移 cf = 0
ja 高于则转移 cf = 0&&zf=0
jna 不高于则转移 cf = 1或zf = 1
25.串传送
movsb将ds:si指向的内存字节送入es:di中,然后根据df位,将si和di递增或递减1
movsw将ds:si指向的内存字送入es:di中,然后根据df位,将si和di递增或递减2
rep movsb
=
s: movsb
loop s
所以rep也是根据CX的值来重复的
assume cs:code, ds:data
data segment
db 'Welcome to masm'
db 16 dup(0)
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, 0
mov es, ax
mov di, 16
mov cx, 16
cld
rep movsb
mov ax, 4c00h
int 21h
code ends
end start
char *g_char = "123456";
int main()
{
char* pCopy = new char[10];
memset(pCopy,0,10);
__asm
{
pushad
pushfd
mov esi,g_char
mov edi,pCopy
cld
mov cx, 6
rep movsb
popfd
popad
}
getchar();
std对应df=1
26.pushf 、popf将标志寄存器压栈、出栈,i386下是pushfd、popfd