debug
a 汇编指令
r 查看寄存器
- r 寄存器
d 查看内存
- d 段地址:偏移地址 (结尾偏移地址)
t 执行指令
e 修改内存数据
e 段地址:偏移地址 数据1 数据2 数据3…
e 段地址:偏移地址
数据 空格 数据 空格(逐个询问)
g执行到结束
寄存器
通用:ax, bx, cx, dx, ip, sp
段:cs(control), ds(data),ss(stack)
栈
系统会自动改动sp,但是不会检测是否越界,无论是栈顶还是栈底,没有这样的寄存器!pop和push只支持字数据
push ax //ax中的数据送入栈,sp-=2 |
程序
- 当常量作为偏移地址的时候,最好加上段前缀,不然会当作把常量的值给寄存器
- 在多个segment的程序中,start从code段开始,且不需要初始化cs,但是ds和ss是需要初始化的。
- 开始以后ds对应的就是程序的起始地址,ds+100H才是真正的data段起始地址
字符处理
- 大写字母+20H = 小写字母(ASCII)
- 小写转大写
and al, 11011111b
- 小写转大写
or al, 00100000b
- 小写转大写
寻址
bp,bx称为基址,si、di称为变址 idata是立即数 。要么单独用,要么基址变址,要么加个立即数,没有基址基址,没有变址变址,没有立即数立即数。注意bp默认是ss段
mov ax,[200+bx] |
变址寄存器
si di可以和bx类似的寻址用法,不同的是,si di只能作为16位来用,而bx可以拆开
dup
伪指令,用来数据重复
db 3 dup(0) |
转移指令
首先认识一下offset,在程序中用s来作为标号(例如start之类的就是标号),
offset s
可以取出s的地址。
jmp
这是z |
jcxz
有条件转移指令,当cx为0跳转,否则继续下一步。
短转移,机器码保存的是位移,范围是-128-127
loop
cx减一,然后判断cx是否为0,如果不是,则跳转,否则继续向下执行。
短转移,负数用补码
双重循环的时候需要注意,cx的值会被你改变了,所以可以用dx来保存cx的值,或者拿一个内存空间来保存cx,或者拿一个栈,push+pop来保存cx,建议用栈。
call
- call + 标号(相对地址 16位)
- 将当前的ip或者cs ip压栈(pop cs pop ip)
- 跳转,最后通过ret返回
- call far ptr段间转移
- call + 寄存器
- call word ptr ds:[0]
- call dword ptr ds:[0]
ret
- ret的本质是从栈内取出ip的值,所以即便没有call,也可以去ss sp指向的位置取数据,相当于
pop ip
- retf段间转移
pop ip
然后pop cs
寄存器冲突
模块化设计中,由于子程序可能修改原程序的寄存器,所以需要在开始时将寄存器入栈,最后再出栈。
标志寄存器
8086中,标志寄存器是16位的,但没有全用。。1,3,5,12-15没有用。
会影响标志寄存器的:加减乘除与或(运算指令)
不影响的:mov push pop(传送指令)
具体来看几个标志
奇偶: 结果中1的个数为o数时,pf为1,否则为0
符号:将结果视为有符号数,若为负数,sf为1
进位:将数据视为无符号数,有进位或者借位的时候cf为1
溢出:将数据视为有符号数,若有溢出,则of为1
指令系统
传送指令
MOV
MOV DST SRC
- dst不能是cs
- dst不能同时为内存单元,也不能同时为段
- 立即数不能直接送段,要通过ax
XCHG
XCHG OPER1,OPER2
- 不影响标志位
- 不能用段寄存器
- 不能同时为内存单元
LEA
LEA REG,SRC 将偏移地址送入寄存器,也可以用mov ax,offset 标号,来传送地址
不影响标志位
REG不能为段寄存器
SRC是寻址方式
32送16时,取低16位
16送32时,0扩展
LDS
LDS REG,SRC 将src处的内存单元里的数据送入reg(通常为si),并且把src+2的数据送入ds
LES
LES REG,SRC 同上,si换成di,ds换成es
算术指令
加法指令
add adc inc
其中adc会加上cf,inc是加一
只有inc不影响标志位
这里考虑一下双精度两个数的加法
add ax,cx |
减法指令
sub dst,src减法
sbb dst,src带借位减法
dec opr减一
以上几个和加法类似
neg opr求补,求补码,相当于0-opr,所以neg可以求负数的绝对值
cmp opr1,opr2比较
需要注意的是,加法减法的dst和src要求和mov一样。另外,sbb和adc主要用于双精度,对于高位的运算
乘法指令
MUL 无符号数
IMUL 带符号数
- 只影响cf,of
- 对于mul,当乘积高一半是0时,cf、of都是0,否则都是1
- 对于imul,当乘积高一半时低一半的符号扩展时,cf、of是0,否则是1
格式:
mul reg
mul 内存单元(需要加word ptr等指定数据类型)
不能是mul idata
结果:
存放在ax或dx、ax中
除法指令
基本同乘法
另外需要说一下符号扩展,由于被除数字长必须是除数两倍,所以需要符号扩展
指令:
cbw:al->ax
cwd:ax->dx,ax
扩展方式:
无符号数:高位置零
有符号数:高位是低位符号的扩展
其实也就是低位的最高位是0就补0,不然就补1
注意:
不影响标志位
定义数据指令
数据标号 db、dw(单字,注意dword是双字)、dd(双字)、dq(四字)、dt(十个字节) 操作数1 操作数2….这里可以用dup具体用法上边写过了,另外直接对标号使用用的是地址
操作数可以是常数、或者表达式
属性修改运算符
说明:类型可以是BYTE、WORD、DWORD、NEAR、 FAR…
示例: DATA1 DB 26H DATA2 DW 5030H MOV AX, WORD PTR DATA1 ADD BYTE PTR DATA2, BL
上例中的: MOV AX,WORD PTR OPER1+1 MOV AL, BYTE PTR OPER2
表达式赋值伪操作
- 等值语句EQU
用来给表达式赋予一个名字
例:
⑴ PORT EQU 1234
⑵ BUFF EQU PORT+58
⑶ MEM EQU DS:[BP+20H]
⑷ COUNT EQU CX
⑸ ABC EQU AAA
等号语句
NUM=34
NUM=34+1
EQU不能重复定义,=可以
ORG
如下,也就是将aa的数据存放在了偏移地址为20的内存中。
org 20
aa dw 2
org 40
bb db,?
属性返回置运算符
seg
格式: SEG 变量/标号 n
功能:返回变量或标号的段基址 n
示例: MOV AX, SEG X1 MOV BX, SEG ARRAY 如果变量X1所在段的段基址为0915H,变量 ARRAY所在段的段基址为0947H,那么上面两条 指令汇编后就分别为: MOV AX, 0915H MOV BX, 0947H
offset
同上,返回偏移地址
type
返回变量或标号的类型属性值
如果该表达式是变量,则返回该变量的 以字节数表示的类型,DB为1,DW为 2 ,DD为4,DF为8….
如果表达式是标号,则返回代表该标号 的数值类型,NEAR是-1,FAR为-2。
如果表达式是常数,则应该回送0
length
如果只有一个dup,则返回dup的个数,否则返回1?还是返回第一个?
格式:length 变量
该指令仅加在变量前,对于变量中使用DUP 的情况,返回分配的单元数(例如 3 dup(?))则返回3,否则仅返回其中一项数据的情况
size
字节总数:type*length
地址计数器
$
指令中表示该指令的首字节地址
数据中表示即将分配出去的地址值
逻辑运算指令
逻辑非指令:NOT OPR * OPR不能为立即数
执行操作: (OPR) ¬ ¬ (OPR) * 不影响标志位
逻辑与指令:AND DST, SRC
执行操作: (DST) ¬ (DST) Ù (SRC)
逻辑或指令:OR DST, SRC
执行操作: (DST) ¬ (DST) Ú (SRC)
异或指令: XOR DST, SRC
执行操作: (DST) ¬ (DST) “ (SRC)
测试指令: TEST OPR1, OPR2 (只改变标志寄存器的AND)
执行操作: (OPR1) Ù (OPR2)
移位指令
格式:例如SHL OPR, CNT(一个是操作数,一个是移位次数(二进制))
逻辑左移:SHL(最高位移入cf,低位补0)
逻辑右移:SHR(相反)
算数左移:SAL(同逻辑左移)
算数右移:SAR(最低为移入cf,最高位不变)
循环移位指令
ROL,ROR(不带进位,类似于不带头节点的循环链表)
RCL,RCR(带进位,类似于带头节点的循环链表)
dos调用
单字符输入
MOV AH,1H
INT 21H
送入的字符存放在AL
单字符输出
MOV DL,’A’
MOV AH,2H
INT 21H
事先把字符送入DL
大小写转换
大写字母二进制和小写字母的二进制不同在于,小写左数第三位是1,大写是0,所以可以通过AND,OR来转换,也可以通过加减20H。
字符串输入
调用10号功能
首先定义缓冲区
BUFFER DB 10,?,10 DUP(?)
其中10代表最大字符数,?处系统会自动填入实际输入的字符数。若输入字符超过缓冲区能容纳的 个数,则系统忽略此字符并响铃警告。
MOV AX,SEG BUFFER |
显示字符串
调用9号功能,要以$结尾
DISPLAY DB ‘Very Good!’ , ‘$’ |
十进制加减运算
在计算机中采用BCD码来表示十进制数。BCD码就是使用四位二进制数表示一位十进制数。在8086/8088系统中,将BCD码分为两种格式:
压缩型(组合型、装配型、PACKED)
一个字节表示两个BCD码,即两位十进制数,例如:0010 0011 表示十进制数的23
非压缩型(非组合型、拆散型、UNPACKED)。
一个字节的低四位表示一个BCD码, 而高四位对所表示的十进制数没有影响。 所以对于非压缩形,09h和’9‘代表的是一样的,因为高位的3忽略了。常为0000B或0011B。例如:0000 1001与0011 1001都是十进制数9的非组合型的BCD码。
压缩BCD的加法调整
压缩的BCD码加法调整
格式:DAA
功能:
如果AL的低4位大于9,则将AL加6,并将辅助进位标志AF置1 (因为相当于16进制,9就相当于15,也就是把16进制的数转化为10进制)
如果AL的高4位大于9,将AL加60H,并将进位标志CF置1(同上,调整的是十位所以加了60h,上一个是06h)
注意:
在执行DAA指令前,必须是用ADD或ADC完成了加 法操作,且加的结果放在AL中
X DB 05H |
压缩BCD的减法调整
压缩的BCD码减法调整
格式:DAS
功能:
如果AL的低4位大于9,则将AL减6,并将辅助进位标志AF置1 (因为相当于16进制,9就相当于15,也就是把16进制的数转化为10进制)
如果AL的高4位大于9,将AL减60H,并将进位标志CF置1(同上,调整的是十位所以加了60h,上一个是06h)
十进制计算62-38=24 |
非压缩BCD的加法调整
非压缩的BCD码加法调整
格式:AAA
功能:
如果AL的低4位大于9或者af=1(辅助进位标识),将AL加6、AH加1,AL的 高4位清零、CF、AF置1 由于非压缩的BCD码用1个字节表示1个十进制数, 所以调整后若加上30H就是该数值的ASCII码。因为0的ascii码是30h
注意:
在AAA指令执行前,必须是使用ADD或ADC指令完成了 加法,且结果是在AL中。AAA指令对AL中内容进行校正
例 十进制计算6+8=14,用非压缩的BCD码表示并显示在屏幕上。 |
非压缩BCD的减法调整
非压缩的BCD码减法调整
格式:AAS
功能:
如果AL的低4位大于9,将AL减6、AH减1, AL的高4位清零、CF、AF置1
例 十进制计算57-18=39,用非压缩的BCD码表示。 |
非压缩BCD的乘法调整
格式:AAM (ASCII Adjust Multiply)
功能: 将乘积AX中的2个非压缩的BCD码调整。 AL除以0AH,得到的商送AH,余数送入 AL。即乘积的高位数在AH 、低位数在 AL中
例 十进制乘法6×8=48,用非压缩的BCD码表示,并显示。 |
非压缩BCD的除法调整
格式:AAD (ASCII Adjust Division)
功能:在做除法之前,将被除数AX中的2 个非压缩的BCD码调整。 (AL)=(AL)+(AH)*10,AH清零。除法之 后,商在AL 、余数在AH中.
考试复习
标号
对于数据标号,即a db 1,2,3
,则在代码段中的a就会被替换成data:[0],前提是要在assume里面关联ds与data。被替换以后,你就懂了吧,它可以表示一个数据,也可以用来寻址a[si]
也就是cs:0[si]
offset
offset只能和数据标号搭配,而不能用复杂的例如ds:[bx]等。。offset取出的地址可以用来mov、add之类的。。
寻址
push word ptr 20[bx+si-2]这里是可以写表达式的
栈
栈顶应该是sp-1吗
异或
XOR CX,CX
相当于mov cx,0,但是异或效率更高
标号
X1 DB 12H, 34H, 56H
X2 DW 78H, 90H
ADR1 DW X1
ADR2 DW X2
如上,ADR1和ADR2表示的是X1和X2的地址,即偏移地址,0000和0300。