这两天花了点时间学习了一下汇编语言,用的是 王爽老师著的汇编语言(第三版),整个学习是基于8086/88 CPU 16 位微机,在此做一下总结,如有错误,欢迎指正。
一、寄存器
8086/88 CPU 中共有 14 个16位寄存器,AX, BX, CX, DX, SI, DI, SP, BP, IP, CS, SS, DS, ES, PSW
其中 通用数据寄存器 有 AX, BX, CX, DX。 可分为8 个 8 位寄存器 AH, BH, CH, DH (高八位)和 AL, BL, CL, DL(低八位)。
通用地址寄存器 有 BP, SP, SI, DI
段寄存器 有 CS, DS, ES, SS
指令指针寄存器 有 IP
标志寄存器 PSW (程序状态字)
因为8086 CPU 有20 位地址总线,可以传送 20 位地址,达到 1MB 的寻址能力,但8086CPU 又是 16 位结构,无法一次发出 20位地址,所以就由两个16 位地址合成 20位地址,这两个16 位地址一个称为段地址, 一个称为偏移地址,通过地址加法器进行合成 物理地址。
物理地址 = 段地址(SA) * 16 + 偏移地址(EA)
在内存中数据和指令都是由二进制构成,那么CPU 怎么判断该数据是指令还是数据呢?这就要看由什么寄存器指向该地址,数据寄存器指向的是数据,指令寄存器指向的是指令
不可直接修改CS 或 IP 的值
接下来,对于每一个寄存器,单独拿出来说说
AX : AH AL 累加器
eg: mov ax,0001h
mov ds,ax
add ax,ax
BX :BH BL 基址寄存器
eg: mov ax,[bx] 等价于 (ax) = ((ds)*16 + (bx))
inc bx bx 中的内容加一
CX : CH CL 计数器
eg: mov cx,12
s:add ax,ax
loop s 循环执行标记 s, 首先 (cx) = (cx)-1, 再判断 cx 中的值,如果cx 不为 0,则转到s 所标识的地址处执行,否则则执行下一条指令
DX : DH DL 数据寄存器,和AX 类似
BP : 基址指针,主要用于存放地址和基址(默认相对与ss 段)
SP : 栈顶指针,与ss 段寄存器配合,共同指向栈顶地址元素
DI : 目的变址寄存器,类似于 BX, 但不可以分为高八位和低八位
SI : 源变址寄存器,和DI 类似
下面三组实现了相同的功能:
(1) mov bx,0
mov ax,[bx]
mov ax,[bx+123]
(2) mov si,0
mov ax,[si]
mov ax,[si+123]
(3) mov di,0
mov ax,[di]
mov ax,[di+123]
CS : 代码段寄存器, 指示了CPU 当前要读取的指令的地址,在任意时刻,CPU 将CS:IP 指向的内容当做指令来执行, 与IP 指令指针寄存器配合使用
DS : 数据段寄存器, 通常用于存放要访问的数据的段地址,
eg: mov bx,1000h
mov ds,bx
mov al,[0] 将10000H(1000h:0)中的数据读到 al 中, ds:[0]
ES : 附加数据段寄存器, 对于DS 的一个附加
SS : 堆栈数据段寄存器,任意时刻,ss: sp 指向的是栈顶的元素,ss: [0] 指向的是栈段的开始地址
eg: mov ax,1000h
mov ss,ax 设置栈的段地址
mov sp,0010h 设置栈顶的偏移地址
IP : 指令指针寄存器,与CS 寄存器配合,指向CPU 当前要读取的指令的地址
PSW: 标志寄存器,也称为状态转移字,决定是否跳转,有三个控制标志(IF, DF, TF), 六个状态标志(SF, PF, ZF, OF, CF, AF)
IF : 中断允许标志,控制CPU 是否响应可屏蔽中断请求,IF = 1 能响应,IF = 0 不能响应
DF : 方向标志,控制串操作时源串和目的串的变址方向, DF = 1 向减的方向,DF = 0 向加的方向
TF : 陷阱标志(单步中断标志),TF = 1 执行当前指令后暂停,TF = 0 执行当前指令后不会暂停
SF : 符号标志,指令执行结果的最高二进制是0 还是 1,为0,则SF = 0,代表正数,为 1,则SF = 1,代表负数
PF : 奇偶校验标志,指令执行结果中低八位中 1 的个数是奇数个还是偶数个,若为奇数个则 PF = 0,若为偶数个则 PF = 1
ZF : 零标志,指令执行的结果是不是 0, 若为 0,则 ZF= 1,否则 ZF = 0
OF : 溢出标志, 有符号数的溢出标志,执行指令的结果是否超出有符号数的表示范围,超出则OF = 1,否则OF = 0
正加正得负,正减负得负,负加负得正,负减正得正,若出现则OF = 1, 否则OF = 0
CF : 进位/错位标志,无符号数的溢出标志,指令执行结果的最高位是否有向更高位进位或者错位,
若有则CF = 1, 同时也代表无符号数溢出,若无则CF = 0,也代表无符号数未溢出
AF : 辅助进位/错位标志,低四位二进制是不是有向高位进位或错位,若有则AF = 1,否则AF = 0,其主要用于BCD 修正运算
eg:
(1) 58H + 3CH = 94H SF = 1, PF = 0, ZF = 0, OF = 1, CF = 1, AF = 1
(2) 0039H - FCE8H = 0351HDW SF = 0, PF = 0, ZF = 0, OF = 0, CF = 1, AF = 0
(3) 35H + CBH = 00H SF = 0, PF = 1, ZF = 1, OF = 0, CF = 1, AF = 1
在DEBUG 中,查询PSW, 对应的标志寄存器状态
标志 1 0
OF OV NV
SF NG PL
ZF ZR NZ
PF PE PO
CF CY NC
DF DN UP
IF EI DI
AF AC NA
二、DEBUG
A(Assemble) 逐行汇编
C(Compare) 比较两内存块
D(Dump) 内存 16 进制显示
E(Enter) 修改内存字节
F(fin) 预置一段内存
G(Go) 执行程序
H(Hexavithmetic) 制算术运算
I(Input) 从指定端口地址输入
L(Load) 读盘
M(Move) 内存块传送
N(Name) 置文件名
O(Output) 从指定端口地址输出
Q(Quit) 结束
R(Register) 显示和修改寄存器
S(Search) 查找字节串
T(Trace) 跟踪执行
U(Unassemble) 反汇编
W(Write) 存盘
就一些较为常用的命令进行详细的说明
-A[address] 该命令允许键入汇编语言语句,并把他们汇编成机器代码,相继的存储在从指定地址开始的存储区中。
DEBUG 把键入的数字都看成十六进制,所以要键入十进制数,则要在其后面加以说明,如100D
-D[address] 或 -D[range] 显示存储单元内容,如果没有制定段地址,自动显示DS 段的内容,如果只指定首地址,则显示从首地址开始的80 个字节的内容,如果完全没有指定地址,则显示上一个D 命令显示的最后一个单元后的内容
-E address [list] 或 -E address 该命令用以修改存储单元的内容
第一种是用给定的内容来代替指定范围内的存储单元内容
第二种是逐个单元相继修改的方法,再按空格后修改下一个单元的内容
-G[=address1][address2][address3...> 运行命令,其中address1 指定了运行的起始地址,如不指定则从当前的CS:IP 开始运行,后面的地址均为断点地址,当执行到断点时,就暂停执行并显示当前所有寄存器及标志位的内容,和下一条要执行的指令
-Q 退出DEBUG,返回DOS,该命令没有存盘功能,如需存盘应先使用W 命令
-R 或 -R range name 或 -RF 查看/修改寄存器
第一种是显示CPU 内所有寄存器内容和标志位状态,
第二种是显示和修改某个寄存器的内容,即显示寄存器 的内容,如不需修改则键入Enter, 否则键入欲修改的内容
eg:-R BX
BX 0369
:059F
第三种是显示和修改标志位的内容,如不需修改则键入Enter, 否则键入欲修改的内容
eg:-RF
OV NG ZR PE CY DN EI AC -PONZUP
-T[=address] 或 -T[=address][value] 跟踪命令
第一种是逐条指令跟踪,从指定地址执行一条指令后停下来,显示所有寄存器内容和标志位的值,如果没有指定地址,则从当前的CS:IP开始执行
第二种是多条指令跟踪,从指定地址执行n 条指令后停下来,n 由 value 指定
-U[address] 或 -U[range] 反汇编命令
第一种是从指定地址开始,反汇编 32 个字节,如果地址被省略,则从上一个 U 命令的最后一条指令的下一个单元开始显示 32 个字节
eg:-U100
第二种是对指定范围内的存储单元进行反编译,
eg:-U100 112 或 -U100 10c