寄存器

一、分类


  1. 通用寄存器:AX,BX,CX,DX
  2. 段寄存器:CS,SS,DS,ES
  3. 其他(待补充):SP,IP
  4. 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: 查看、改变寄存器的内容
2. -d: 查看内存中的内容
3. -e: 修改内存中的内容
4. -u: 将机器码翻译为汇编指令
5. -t: 执行一条汇编指令
6. -a: 以汇编指令格式写入机器指令
7. -p: 结束执行程序
8. -q: 退出debug



四、栈


  1. SS存放栈顶的段地址,SP存放偏移地址,即SS:SP指向栈顶元素,栈空时,SS指向最高位,SP指向最低位。
  2. 注意1000-100F为十六个字节,定义栈空间大小后,CPU不会自动阻止超界,注意栈空间的管理。
  3. push R/SR/Unit ;入栈
    pop R/SR/Unit ;出栈
  4. mov ax,2000
    mov ss,ax
    mov sp,10 ;安排2000:0000~2000:000F为栈空间,初始化栈顶

程序基础

一、知识点


  1. 源程序中包含两种指令,一种​​汇编指令​​,一种是​​伪指令​​,前者有机器码对应,由CPU执行;后者没有机器码,仅通过编译器执行。
  2. 伪指令
    segment 和 ends:一个段的开始和结束。
    end: 区别于ends,end表示汇编程序的结束(ends可理解为end segment)。
    assume: 可将段与寄存器关联起来,如:​​assume cs: codesg​​对应​​将codesg段与cs寄存器关联​​。
  3. 程序加载后,DS中存放程序所在内存区的段地址,PSP(通信区占256个字节)为占以为(指令位置一个单元为1kb),如ds=075A,则​​段地址sa*16+偏移地址ea=cs地址​​,cs=076A
  4. loop指令实现循环功能,cx存放循环次数。cx==0才向下执行。此方式每次都先执行循环内容,在loop语句cx减1
    用法实例:


mov cx,12

s: add ax,ax

loop s




用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
mov ds,ax

mov ax,[0] ; == mov ax,0(编译器masm中,即段首缺省时[idata]解释为idata)
mov ax,ds:[0] ; 最正确的方式


首先看看:状态寄存器(即标志寄存器)

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

code segment

xxx数据 ;在这个区域存放数据

start:

xxx代码 ;在这个区域存放代码

code ends
end start


数据、代码、栈放在不同的段

一个段的容量不能大于64KB,这是8086的限制

所以写程序的时候为了安全 将数据和代码还有栈分在不同空间是一种美德

下面示范


assume cs:code,ds:data,ss:stack

data segment

dw 11h,22h,33h,44h,55h,66h,77h,88h ;数据段

data ends

stack segment

dw 0,0,0,0,0,0,0,0 ;栈段

stack ends

code segment

start: xxx
xxx
xxx

code ends

end start


细节说明

  1. 对于段地址的引用




;正规操作
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. 对于字母的大小写转换, 小写和大写之间在二进制有第五位为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
db 3 dup (0,1,2) ;等价于db 0,1,2,0,1,2,0,1,2 3*3 = 9个字节
db 3 dup ('abc','ABC') ;等价于db abcABCabcABCabcABC 3*6 = 18个字节


常见于初始化大片区域为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
;低位段地址: 0BBD
;高位偏移地址: 010B


Call 和 RET

都是转移指令

ret 和 retf

都是利用栈种的数据

ret 修改 IP, 实现​近转移​,实质如下:


pop IP


retf 修改CS:IP, 实现​远转移​,实质如下:


pop IP
pop CS ;注意顺序


Call指令

操作分为两步


  • 将下一条地址的位置入栈
  • 跳转

不能实现短转移, 至少为16位的近转移

call + 标号

等价于


push ip
jmp near ptr x


call far ptr + 标号

等价于


push cs
push ip
jmp far ptr


内中断

产生方式大致有四种:


  • 除法溢出 对应中断类型码0
  • 单步执行 对应中断类型码1
  • into指令 对应中断类型码4
  • int x 对应中断类型码 x

开始时

先入栈EFLAGS寄存器

然后入栈CS 和 IP

中断例程的最后会用到iret指令

相当于


pop IP
pop CS
popf ;恢复标志寄存器


端口

访问端口与通常访问寄存器的方式不同

通常的mov, lea等访问指令都不能用来访问端口

访问端口仅有两条指令

​in​​ 用于读入数据到寄存器

​out​​ 用于将数据写入端口

CMOS RAM

该RAM芯片提供两个端口

分别为​​70h​​​和​​71h​

​70h​​为 地址端口

​71h​​ 为数据端口

通过这两个端口可以实现数据的读写


  • 将2送入端口70h
  • 从71h可以读取到2号单元的内容

外中断

PC系统中,外中断源分为两类


  1. 可屏蔽中断
    多种中断类型码
  2. 不可屏蔽中断
    中断类型固定为2

键盘处理

键盘输入 产生扫描码

送入60h端口引发可屏蔽中断 中断类型码为9