目录:
第5章 [bx]与loop指令
要完整的描述一个内存单元,需要两种信息:(1)内存单元的地址;(2)内地单元的长度(类型)。
- [0]表示一个内存单元时,0表示单元的偏移地址,段地址默认在ds中,单元的长度(类型)由具体指令中的其他操作对象(比如寄存器)指出。
- [bx]同样也表示一个内存单元,偏移地址在bx中,段地址在ds中 如:
mov ax,[bx] //2个字节
mov al,[bx] //1个字节
loop指令格式:loop 标号,CPU执行loop指令的时候,要进行两步操作:(1)(cx)= (cx)-1; (2)判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。
如:
mov cx,循环次数
s: 循环执行的程序段
loop s
说明:标号代表一个地址,如上的代码中标号s,标识了一个地址,这个地址处有循环执行的程序段。
汇编程序,MASM数据不能以字母开头,如:0A00h,但在debug中可以
DeBug中命令
g 偏移地址 —— 表示执行程序到当前代码段(段地址在CS中)的偏移地址中
p命令——Debug会自动重复执行循环中的指令
汇编源程序指令含义:
mov al,[0] #(al)=0,将常量0送入al中(与mov al,0含义相同)
mov al,ds:[0] #(al)=((ds)*16+(bx)),将内存单元中的数据送入al中
mov al,[bx] #(al)=((ds)*16+(bx)),将内存单元中的数据送入al中
mov al,ds:[bx] #与“mov al,[bx]”相同
段前缀:出现在访问内存单元的指令中,用于显式地指明内存单元的段地址的“ds:”、”cs:”、”ss:”、“es:”。
第6章 包含多个段的程序
程序取得所需空间的方法:
- 加载程序的时候为程序分配(定义段)
- 程序执行过程中向系统申请
从规范的角度讲,我们不能自己随意决定哪段空间可以使用,应该让系统来为我们分配。我们可以在程序中,定义我们希望处理的数据,这些数据就会被编译、连接程序作为程序的一部分写到可执行文件中。当可执行文件中的程序被加载入内存时,这些数据也同时被加载入内存中,此时数据也就获得了存储空间。
dw #定义字型数据,即define word(2个字节)也可以说是开辟了2个字节的内存空间
//例如:
// dw 0123H,0456H 定义了2个字型数据
end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方
在单任务系统中,可执行文件中的程序执行过程如下:
1. 由其他程序将可执行文件中的程序加载入内存
2. 设置CS:IP指向程序的第一条要执行的指令(即程序的入口),从而使程序得以运行
3. 程序运行结束后,返回到加载者
程序的框架如下:
assume cs:code
code segment
.
.
数据
.
start: ...
代码
...
code ends
end start
将数据、代码、栈放入不同的段:
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h
data ends
stack segment
dw 0,0,0,0
stack ends
code segment
start : ...
...
...
code ends
end start
说明:
1. 定义一个段的方法和我们之前定义代码段的方法没有区别只是对于不同的段,要有不同的段名
2.程序中的多个段通过地址引用,地址分为两部分:段地址和偏移地址。程序中段名相当于一个标号,代表了段地址,所以我们可以使用如下代码指明数据段:
mov ax,data
mov ds,ax
其中“data”被编译器处理为一个表示段地址的数值,不能使用
mov ds,data
3.程序中我们定义的“code”、“data”和“stack”只是为了方便阅读,伪指令“assume cs:code,ds:data,ss:stack”由编译器执行,将cs、ds和ss分别和code、data、stack段相连。
4. 源程序最后用“end start”说明程序入口,这个入口被写入可执行文件的描述信息,可执行文件中的程序被加载入内存后,CPU的CS:IP被设置指向这个入口,从而开始执行程序的第一条指令。标号“start”在code段中,这样CPU就将code段中的内容当作指令来执行。在code段中使用指令:
mov ax,stack
mov ss,ax
mov sp,16
设置ss指向stack,设置ss:sp指向stack:16,CPU指行这段指令后,将把stack段当作栈空间来使用,同样data段也是如此。
总结:CPU如何处理我们定义的段中的内容,是当作指令执行,当作数据访问,还是当作栈空间,完全是靠程序中具体的汇编指令,和汇编指令对CS:IP、SS:SP、DS等寄存器的设置来决定的。
第7章 更灵活的定位内存地址的方法
and和or指令
(1)and指令:逻辑与指令,按位进行与运算
通过该指令可将对象的相应位设为0,其他位不变
and al,10111111B #将al的第6位设为0
and al,01111111B #将al的第7位设位0
(2)or指令:逻辑或指令,按位进行或运算
通过该指令可将操作对象的相应位设为1,其他位不变
or al,01000000B #将al的第6位设为1
or al,10000000B #将al的第7位设为1
在汇编程序中,用’……’方式指明数据是以字符的形式给出的,编译器将把它转化为相应的ASCII码。
db 'unIX' #相当于 db 75h,6eh,49h,58h
db 'foRK' #相当于 db 66h,6fh,52h,4bh
mov al,'a' #相当于 mov al,61h
mov bl,'b' #相当于 mov al,62h
一个字母不管原来是大写还是小写,如果将第5位置0,那么它就必将变为大写字母;将第5位置1,它就必将变为小写字母
小写 or 00100000 大写 and 11011111
[bx+idata] #表示一个内存单元,他的偏移地址位(bx)+idata,即bx中的数值加上idata,段地址在ds中格式有:
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
si和di是8086CPU中和bx功能相近的寄存器,si和di不能够分成两个8位寄存器来使用,使用格式和bx相同。
使用方式
[bx+si]
[bx+di]
[bx][si]
[bx][di] #偏移地址位(bx)+(si),即bx中的数值加上si中的数值
----------
[bx+si+idata] #di类似
idata[bx][si]
[bx].idata[si]
[bx][si].idata #偏移地址位(bx)+(si)+idata,即bx中的数值加上si中的数值再加上idata
总结:
(1)[idata]用一个常量来表示,可用于直接定位一个内存单元
(2)[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元
(3)[bx+idata]用一个变量和常量表示地址,可以在一个起始地址的基础上用变量间接定位一个内存单元
(4)[bx+si]用两个变量表示地址
(5)[bx+si+idata]用两个变量和一个常量表示地址
一般来说,需要暂存的数据,应该使用栈来存放
第8章 数据处理的两个基本问题
计算机进行数据处理、运算时要考虑两个问题:
(1)处理的数据在什么地方
(2)要处理的数据有多长
说明:
(1)bx、si、di和bp用来进行内存单元的寻址,这四个寄存器可以单个出现,或只能以四种组合出现:bx和si、bx和di、bp和si、bp和di
(2)bp作为偏移地址,段地址默认在ss中
寻址方式 | 含义 | 名称 | 常用格式举例 |
[idata] | EA=idata;SA=(ds) | 直接寻址 | [idata] |
[bx] [si] [di] [bp] | EA=(bx);SA=(ds) EA=(si);SA=(ds) EA=(di);SA=(ds) EA=(bp);SA=(ss) | 寄存器间接寻址 | [bx] |
[bx+idata] [si+idata] [di+idata] [bp+idata] | EA=(bx)+idata;SA=(ds) EA=(si)+idata;SA=(ds) EA=(di)+idata;SA=(ds) EA=(bp)+idata;SA=(ss) | 寄存器相对寻址 | 用于结构体:[bx].idata 用于数组:idata[si],idata[di] 用于二维数组:[bx][idata] |
[bx+si] [bx+di] [bp+si] [bp+di] | EA=(bx)+(si);SA=(ds) EA=(si)+(di);SA=(ds) EA=(di)+(si);SA=(ss) EA=(bp)+(di);SA=(ss) | 基址变址寻址 | 用于二维数组:[bx][si] |
[bx+si+idata] [bx+di+idata] [bx+di+idata] [bp+di+idata] | EA=(bx)+(si)+idata;SA=(ds) EA=(si)+(di)+idata;SA=(ds) EA=(di)+(si)+idata;SA=(ss) EA=(bp)+(di)+idata;SA=(ss) | 相对基址变址寻址 | 用于表格(结构)中的数组项:[bx].idata[si] 用于二维数组:idata[bx][si] |
汇编指令中,可以使用[bx+idata+si]的方式来访问结构体中的数据,bx定位整个结构体,idata定位结构体中的某一个数据项,si定位数据项中的每个元素
机器指令中,一般通过以下方法指明指令处理数据的长度
(1)通过寄存器名指明要处理的数据的长度
mov ax,1 #字操作
mov al,1 #字节操作
(2)在没有寄存器名存在的情况下,用操作符 X ptr
指明内存单元的长度,X
在汇编指令中可以为word
或 byte
mov word ptr ds:[0],1 #指明访问的内存单元是一个字单元
mov byte ptr ds:[0],1 #指明访问的内存单元是一个字节单元
(3)其他方法
有些指令默认访问的是字单元还是字节单元,如push
只进行字操作
div(除法指令)注意事项:
(1)除数:有8位和16位两种,在一个寄存器或内存单元中。
(2)被除数:默认放在AX或DX和AX中,如果除数为8位,被除数则为16位,默认放在AX中;如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位。
(3)结果:如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数;如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数。
div byte ptr ds:[0] #(al)=(ax)/((ds)*16+0)的商
#(ah)=(ax)/((ds)*16+0)的余数
div word ptr es:[0] #(ax)=[(dx)*10000h+(ax)]/((es)*16+0)的商
#(dx)=[(dx)*10000h+(ax)]/((es)*16+0)的余数
伪指令dd用来定义dword(double word,双字)型数据
data segment
db 1 #01h,在data:0处,占一个字节
dw 1 #0001h,在data:1处,占1个字
dd 1 #00000001h,在data:3处,占2个字
data ends
dup由编译器识别处理,和db、dw、dd等数据定义伪指令配合使用,用来进行数据的重复。
#定义3个字节,值为0
db 3 dup (0) #相当于db 0,0,0
#定义9个字节,它们是0,1,2,0,1,2,0,1,2
db 3 dup (0,1,2) #相当于db 0,1,2,0,1,2,0,1,2
#定义18个字节
db 3 dup ('abc','ABC')
dup使用格式如下:
- db 重复的次数 dup (重复的字节型数据)
- dw 重复的次数 dup (重复的字型数据)
- dd 重复的次数 dup (重复的双字数据)
第9章 转移指令的原理
可以修改IP,或同时修改CS和IP的指令统称为转移指令。概括地讲,转移指令就是可以控制CPU执行内存中某处的代码的指令。
8086CPU的转移行为有以下几类:
- 只修改IP时,称为段内转移
- 同时修改CS和IP时,称为段间转移
由于转移指令对IP的修改范围不同,段内转移又分为:短转移和近转移
- 短转移IP的修改范围为-128~127 (8位)
- 近转移IP的修改范围为-32768~32767(16位)
8086CPU的转移指令分为以下几类:
- 无条件转移指令
- 条件转移指令
- 循环指令
- 过程
- 中断
操作符offset 由编译器处理,功能是取得标号的偏移地址。
start : mov ax,offset start ;取start的偏移地址,赋值给ax
jmp指令无条件转移指令,可以只修改IP,也可以同时修改CS和IP
jmp要给出两种信息:
- 转移的目的地址
- 转移的距离(段间转移、段内短转移、段内近转移)
(1) jmp short 标号
段内短转移,对IP的修改范围为-128~127;“short”标号说明指令进行的是短转移,“标号”是代码段中的标号。
(2)jmp near ptr 标号
段内近转移,对IP的修改范围为-32768~32767
(3)jmp far ptr 标号
段间转移,又称为远转移
(CS)=标号所在段的段地址;(IP)=标号在段中的偏移地址
far ptr 指明了指令用标号的段地址和偏移地址修改CS和IP
转移地址在内存中的jmp指令有两种格式:
(1) jmp word ptr 内存单元地址(段内转移)
功能:从内存单元地址处开始存放着一个字,是转移的目的偏移地址
(2)jmp dword ptr 内存单元地址(段间转移)
功能:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址
(cs)=(内存单元地址+2)
(IP)=(内存单元地址)
内存单元地址可用寻址方式的任一格式给出。