x86汇编语言基础
基本规则
- 
寄存器: 
- 
ax bx cx dx(计算常用) 
- ds cs(指令和数据段地址),ss(堆栈地址),es(段寄存器) 段寄存器不可以直接赋值常数或计算,只能用寄存器或变量赋值
- si di bx bp sp (偏移地址寄存器,即可以放进[ ]中) (其中ss:sp指向栈顶,bp也常用于栈中元素)
- ip (指令指针,指令的偏移地址)
- FL (16位记录运算状态)
- 双目运算位宽相等且可确定,并且不能同时为内存地址
  变量强制规定位宽:byte/word/dword ptr
定义的变量某种程度上也属于可确定宽度的,所以可以直接 mov a,0
- assume
  本质上为省略段地址的替换
(同一个段与多个段寄存器有关联时:ds > ss > es > cs)
例如assume ds:data即将data:自动替换为ds: 所以一般程序开头有
数据存储
- 内存中小端规则,低位在前,高位在后
如 1234H ---> 34 12
可用下面两段C语言代码验证
   unsigned short int a = 0x1234;
   unsigned char *p;
   p = (unsigned char *)&a;
   printf("%X %X", p[0], p[1]);
   unsigned char a[2]={0x12, 0x34};
   unsigned short int *p;
   p = (unsigned short int *)a;
   printf("%X", *p);
- 符号不区分
到底代表255还是-1,在定义时并不确定
但是在引用变量a时可以用指令来区分它是非符号还是有符号
例如 imul  a指令表示乘以  -1 ,而 mul a指令则表示乘以 255
- 段地址:偏移地址(逻辑地址寻址) 以5位16进制的形式 XXXX:0000~~XXXX:FFFF 64KB 逻辑地址XXXX0+YYYY 需要注意一个物理地址可以表示成多个逻辑地址
12398h=1234:0058=1235:0048=1236:0038=1230:0098
- 
寻址方式 
- 
直接寻址(偏移地址为常数,段地址必须为段寄存器) 1000H:[2000H]❌ 
- 间接寻址(偏移地址为寄存器+变量/数组)
- 
取地址运算 
- 
lea & offset - lds & les设abc的偏移地址=1000h lea dx, abc ; lea dx, [1000h] mov dx, offset abc ; mov dx, 1000h mov dx, offset ds:[bx+si+3] ; 语法错误 lea dx, ds:[bx+si+3] ; dx=bx+si+3 mov dx, bx+si+3 ; 错误 lea eax, [eax+eax*4] ; EAX=EAX*5 用lea做乘法取远指针,并分别把段地址赋给ds/es,偏移地址赋给相应目标 - 远近指针 
- 
near ptr(偏移地址) 
- 
far ptr (段地址:偏移地址) 假定把一个远指针1234h:5678h存放到地址1000:0000中,则内存布局如下: &p=1000:0000 1000:0000 78h 1000:0001 56h 1000:0002 34h 1000:0003 12h 
算术运算
- inc / dec x单目运算(自加自减,不影响CF)
- add / sub a,b双目运算
- mul x单目运算 根据x宽度确定乘法宽度
div x 单目运算
基本可视为乘法的逆运算,其中dx存余数,ax存商
(注意dx的清空,否则易出现divide overflow报错)
32位十进制输出
   mov di, 0; 数组s的下标
   mov eax, abc
   mov cx, 0; 统计push的次数
again:
   mov edx, 0; 被除数为EDX:EAX
   mov ebx, 10
   div ebx; EAX=商, EDX=余数
   add dl, '0'
   push dx
   inc cx; 相当于add cx, 1
   cmp eax, 0
   jne again
pop_again:
   pop dx
   mov s[di], dl
   inc di
   dec cx; 相当于sub cx, 1
   jnz pop_again
&     |     ^      ~      <<     >>
  and   or    xor    not    shl    shr
简单位运算
(移位运算最后移出的一位存到CF中) - ``` rol ror
用这些指令来完成十六进制输出:
again:
   rol ax, 4  ;取出最高4位到低位
   push ax
   and ax, 000Fh
   cmp ax, 10
   jb is_digit
is_alpha:
   sub al, 10
   add al, 'A'
   jmp finish_4bits
is_digit:
   add al, '0'
finish_4bits:
   mov s[di], al
   pop ax
   pop cx
   add di, 1
   sub cx, 1
   jnz again
堆栈
- push x
16位或32位,sp=sp-sizeof(x)
- pop x
16位或32位,sp=sp+sizeof(x)
- pushf & popf 保护FL状态寄存器
- 手动定义堆栈空间
FL状态寄存器
CF ZF SF OF AF PF(状态) DF TF IF(控制)
mov指令不影响任何标志位
- CF(Carry Flag) 加法进位,减法借位
奇偶标志,可做校验,低八位中的1的个数,1 --> 有偶数个1 - AF(Auxiliary Flag)
辅助进位标志,1 --> 第四位与高四位进位或借位时 - DF(Direction Flag) 字符串复制方向
- IF(Interrupt Flag) 1 --> 允许中断 - TF(Trap Flag) 1 --> 进入单步模式,每条指令后隐含int 1H(callback) 用于debug,反调试 被动防御:校验代码防止更改 主动防御:抢夺资源,自己获得int 1H主动权字符串操作
- xlat(查表)
al=ds:[bx+al] - movsb movsw movsd(宽度不同)
①ds:si 源字符串(si就是source index) ②es:di 目标字符串(di就是destination index) ③cx 移动次数 ④DF=0 即方向标志设成正方向(用指令cld) - stosb stosw stosd(宽度不同)
mov es:[di], al/ax/eax
关于循环
loop label    ;while(--CX) jmp label
loopz label   ;while(ZF && --CX) jmp label
loopnz label  ;while(!ZF && --CX) jmp label
常用中断
等效操作
pushf, push cs, push ip
tf = 0, if = 0
ip = word ptr 0:[n*4], cs = word ptr 0:[n*4+2]
mov ah,01H
int 21H           ;al=getchar()
mov ah,02H
int 21H           ;putchar(dl)
mov ah,09H
int 21H           ;cout << ds:[dx]-->'$'
mov ah,0AH  
int 21H           ;getline( ds:[dx+2] )
          ;其中ds:[dx]为最大读入量(记得置一个大数)
          ;ds:[dx+1] 为实际读到的字符数 
mov ah,4CH
int 21H           ;exit
mov ah,00H
int 16H           ;AH = BIOS scan code
                  ;AL = ASCII character
mov ah,3DH
int 21H           ;openfile
;AL = access and sharing modes
;DS:DX -> ASCIZ filename
;CL = attribute mask of files to look for
;Return:
;CF clear if successful
;AX = file handle
;CF set on error
;AX = error code (01h,02h,03h,04h,05h,0Ch,56h)
关于BCD码
一种用十六进制表示十进制数的方法,比如端口中时钟
15H   <---->   15D
- 
压缩BCD 4位表示一个十进制位 
- 
daa 加法调整 具体地 
8个二进制位的高四位没用
0306H  <---->  3366H  <----> 36D
- aaa 加法调整
- 具体地
```cpp
AH /= 10
AL %= 10
```
- 
aad 除法 前 预先调整