第四章 第一个程序
1、伪指令,没有对应的机器码的指令,最终不被CPU所执行。伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。
2、segment和ends是一对成对使用的伪指令,这是在写可被编译器编译的汇编程序时,必须要用到的一对伪指令。segment和ends的功能是定义一个段,segment说明一个段开始,ends说明一个段结束。
3、一个段必须有一个名称来标识,使用格式为:
段名 segment
段名 ends
4、一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或当作栈空间来使用。一个有意义的汇编程序中至少要有一个段,这个段用来存放代码。
5、end 是一个汇编程序的结束标记,编译器在编译汇编程序的过程中,如果碰到了伪指令end,就结束对源程序的编译。end 除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。
6、assume:含义为“假设”,它假设某一段寄存器和程序中的某一个用 segment...ends 定义的段相关联。(可以将ends 联想为 end segment的缩写)。
7、通过assume说明这种关联,在需要的情况下,编译程序可以将段寄存器和某一个具体的段相联系。
8、程序编写步骤:定义一个段、实现处理任务、程序结束、段与寄存器关联。
9、连接的作用有以下几个:
a、当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序将它们连接到一起,生成一个可执行文件。
b、程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件。
c、一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这此内容处理为最终的可执行信息。
d、所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。
10、Debug将程序从可执行文件加载入内存后,通用寄存器CX中存放的是程序的长度。数据段寄存器DS中存放着程序所在内存区的段地址SA,这个内存区的偏移地址为0,则程序所在的内存区的地址为:SA:0;这个内存区的前256个字节中存放的是PSP,是DOS用来和程序进行通信的。从256字节处向后的空间存放的是程序。因为PSP占256(100H)字节,所以,实际上程序的物理地址是为:SA+10:0,因此CS中的段地址为SA+10H。
11、标号,在汇编语言中,标号代表一个地址。
第五章 [BX]和loop指令
1、和Debug程序不同,在masm编译器环境中,输入指令"mov ax,[1]"的时候,编译器会将其当成"mov ax,1",因此,我们要在masm中达到在Debug环境中的"mov ax,[1]"指令,我么需要分为两步:“mov bx,1”和"mov ax,[bx]",这里中括号中只能够是bx,不能够是ax等其他寄存器。当然,如果我们要在masm中,不通过[bx]中间寄存器,而直接表示Debug中的"mov ax,[1]"指令的话,我们可以这么写:“mov ds:[1]”。但是我们也只能够使用"mov
ds:[1]"这种格式,不能够将ds的具体段地址加入命令中,也就是说,假如(ds) = 3FFD,我们只能够写成"mov ax,ds:[1]",不能够写成"mov ax, 3FFDH:[1]"。当然了,这种格式下,我们也可以写成"mov ax,ds:[bx]"也是可以的。同时需要注意的是,Debug默认数字为16进制,而Masm默认数字为十进制,因此在Masm中,需要显示的加上H,才能够表示该数字为十六进制,否则masm会将其当成默认的十进制数处理。
2、指令的格式是:"loop 标号",CPU 执行loop指令的时候,要进行两步操作:
a、(cx)=(cx)-1;(即,CX中存储的数字代表循环的次数)。
b、判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。
3、在程序的同一个段中不能够定义两个名称相同的标号。
4、汇编源程序中,数据不能以字母开头,因此不能够在masm中书写"mov ax, ffffh",而要在ffff前面加上0,书写成"mov ax,0ffffh"。
5、段前缀:指令“mov ax,[bx]”中,内存单元的偏移地址由bx给出,而段地址默认在ds中。我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器。这些出现在访问内存单元的指令中,用于显式地指明内存单元的段地址的“ds:”、“cs:”、“ss:”或“es:”,在汇编语言中称为段前缀。
6、当我们需要直接向一段内存中写入内容的时候,这段内存空间不应存放系统或其他程序的数据或代码,否则写入操作很可能引发错误。DOS方式下,一般情况,0:200~0:2FF 空间中没有系统或其他程序的数据或代码;因此,我们需要直接向一段内存中写入内容时,就使用0:200~0:2FF这段空间。
第六章 包含多个段的程序
1、dw即define word,定义字型数据。
2、db即define byte,定义字节型数据。
第七章 更灵活定位内存
1、and 指令:逻辑与指令,按位进行与运算。
2、or 指令:逻辑或指令,按位进行或运算。
3、我们可以在汇编程序中,用两个单引号‘……’的方式指明数据是以字符的形式给出的,编译器将把它们转化为相对应的ASCII码
4、通过对比ASCII编码表,我们可以看出来,小写字母的ASCII码值比大写字母的ASCII码值大20H;这样,如果将“a”的ASCII码值减去20H,就可以得到“A”;如果将“A”的ASCII码值加上20H 就可以得到“a”。
5、如果通过二进制的方式来看待字母的大小写,并定义最右边的最低位为第0位,则对于一个字母,不管它原来是大写还是小写:如果我们将它的第5位置0,它就必将变为大写字母;如果将它的第5 位置1,它就必将变为小写字母。
6、"mov ax,[bx+200]" 和 "mov ax,[200+bx]" 和 "mov ax,200[bx]" 和 "mov ax,[bx].200" 等价。
7、SI和DI是8086CPU中和bx功能相近的寄存器,但是SI和DI不能够分成两个8 位寄存器来使用。
8、作为理解,我们可以真么认为:SI = Source Index,DI = Destination Index。
9、‘mov ax, [bx][si]’ 和 'mov ax,[bx + si]'等价。
10、一个操作指令当中,最多只能够出现两个偏移地址变量,可以出现多个常量,常量可以是负数。'mov ax,[bx][si][di]'不合法,会提示'error A2047: Multiple index registers'。
第八章 数据处理的两个基本问题
1、计算机是进行数据处理、运算的机器,在进行处理和运算的时候,计算机最关心的是:处理的数据在什么地方以及要处理的数据有多长,这两点在机器指令中必须给以明确或隐含的说明,否则计算机就无法工作。
2、在8086CPU 中,只有这4个寄存器(bx、bp、si、di)可以用在“[…]”中来进行内存单元的寻址。
3、在“[…]”中,这4个寄存器(bx、bp、si、di)可以单个出现,或只能以四种组合出现:bx和si、bx和di、bp和si、bp和di,即不能同时出现两个p、两个i。
4、只要在[…]中使用寄存器bp,而指令中没有显性的给出段地址,段地址就默认在ss中。也即bp和sp相似。
5、指令在执行前,所要处理的数据可以在三个地方:CPU内部、内存、端口。
6、立即数(idata):对于直接包含在机器指令中的数据(执行前在CPU 的指令缓冲器中),在汇编语言中称为:立即数(idata = Immediate Data),在汇编指令中直接给出。'mov ax,1'。
7、8086CPU的指令,可以处理两种尺寸的数据,byte和word。所以在机器指令中要指明,指令进行的是字操作还是字节操作。
a、通过寄存器名指明要处理的数据的尺寸。
b、在没有寄存器名存在的情况下,用操作符x ptr显示的指明内存单元的长度,X在汇编指令中可以为word或byte。
c、其他方法,因为有些指令默认访问的字单元或者是字节单元,比如push/pop,它只能够对字进行操作。
8、div是除法指令,使用div作除法的时候:
a、除数:是8位或16位,在寄存器或内存单元中
b、被除数:当除数为8位的时候,被除数默认放在AX,当除数为16位的时候,被除数默认放在DX和AX中。
c、结果:为8位或16位。当除数为8位的时候,商存放于AL中,余数存放于AH中。当除数为16位的时候,商存放于AX中,余数存放于DX中。
9、伪指令dd即define double word,定义双字节(32)位数据。通过8086CPU执行dd定义后的数据,地位存放于低字节,高位存放于高字节。例如:dd 12345678H ,则对应的内存中的存放顺序为:78 56 34 12。
data segment
dd 100001 ;定义一个32位的十进制数100001,对应的十六进制数为186A1H,则8086CPU定以后,数据段的存放顺序为
;data:[0] A1 data:[2] 86 data:[4] 01 data:[6] 00
dw 100 ;定义16位的字型十进制数据100,对应的十六进制数位64H,则8086CPU定以后,数据段的存放顺序为
;data:[8] 64 data:[10] 00
dw 0 ;定义16位的字型十进制数据0,对应的十六进制数位0H,则8086CPU定以后,数据段的存放顺序为
;data:[12] 00 data:[14] 00
;定以后,data段的数据显示为:A1 86 01 00 64 00 00 00
data ends
10、伪指令dup是一个操作符,在汇编语言中同db、dw、dd 等一样,也是由编译器识别处理的符号。它是和db、dw、dd 等数据定义伪指令配合使用的,用来进行数据的重复。
db 重复的次数 dup (重复的字节型数据)
dw 重复的次数 dup (重复的字型数据)
dd 重复的次数 dup (重复的双字数据)
db 3 dup (0) 定义了3个字节,它们的值都是0,相当于db 0,0,0
db 3 dup (0,1,2) 定义了9个字节,它们是0、1、2、0、1、2、0、1、2,相当于db 0,1,2,0,1,2,0,1,2
db 3 dup (‘abc’,’ABC’) 定义了18个字节,它们是‘abcABCabcABCabcABC’,相当于db ‘abcABCabcABCabcABC’
第九章 转移指令的原理
1、8086CPU的转移指令分为以下几类:
a、无条件转移指令(如:jmp)
b、条件转移指令
c、循环指令(如:loop)
d、过程
e、中断
2、offset伪指令,取得标号的偏移地址。
3、jmp为无条件转移,可以只修改IP,也可以同时修改CS和IP。
4、jmp指令要给出两种信息:
a、转移的目的地址
b、转移的距离(段间转移、段内短转移,段内近转移)
5、jmp short 标号(转到标号处执行指令),这种格式的jmp 指令实现的是段内短转移,它对IP的修改范围为-128~127,也就是说,它向前转移时可以最多越过128个字节,向后转移可以最多越过127个字节。
6、在“jmp short 标号”指令经过编译后所对应的机器码中,并不包含转移的目的地址,而包含的是转移的位移。这个位移,占用一个字节的空间,是编译器根据汇编指令中的“标号”计算出来的,编译器在编译时的计算方法为:“标号”处的地址-jmp指令后的第一个字节的地。
8、“jmp short 标号”的功能为(IP)=(IP)+由8个bit位所表示的位移,因此,执行"jmp short 标号"的过程,其实就是一个修改IP的过程。
9、jmp near ptr 标号,它实现的时段内近转移。指令“jmp near ptr 标号”的功能为:(IP)=(IP)+由16个bit位所表示的位移。也就是说,jmp near ptr可以比jmp short偏移的更远,偏移地址可以用16位来表示,范围为-32768~32767。当然,执行"jmp near 标号"的过程,实质上也是一个修改IP的过程。
9、指令“jmp far ptr 标号”,实现的是段间转移,又称为远转移。jmp far ptr指令经过编译器编译后所对应的机器码中指明了要指令用标号处的段地址和偏移地址修改CS和IP。
10、jmp 16位寄存器,这中格式的jmp指令表示,转移到16位寄存器所存储的地址处。
11、转移地址在内存中的jmp指令有两种格式:
a、jmp word ptr 内存单元地址(段内转移),从内存单元地址处开始存放着一个字,是转移目的偏移地址。内存单元地址可用寻址方式的任一格式给出。
b、jmp dword ptr 内存单元地址(段间转移),从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址CS,低地址处是转移的目的偏移地址IP。也即,(CS)=(内存单元地址+2),(IP)=(内存单元地址)。内存单元地址可用寻址方式的任一格式给出。
12、jcxz 标号:为有条件转移指令,所有的有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对IP的修改范围都为-128~127。(如果(cx)=0,则转移到标号处执行)。“jcxz 标号”的功能相当于:“if((cx)==0) jmp short 标号”。
13、loop 标号:为循环指令,所有的循环指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对IP的修改范围都为-128~127。((cx))=(cx)-1,如果(cx)≠0,转移到标号处执行。
14、dec指令和inc指令相反,dec是自减1。
第十章 Call和Ret指令
1、call和ret 指令都是转移指令,它们都修改IP,或同时修改CS和IP。
2、ret指令用栈中的数据,修改IP的内容,从而实现近转移;
3、retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移;
4、CPU执行ret指令时,相当于进行:pop IP
5、CPU执行retf指令时,相当于进行:pop IP,然后pop CS
6、CPU执行call指令,进行两步操作:
a、将当前的IP 或CS和IP 压入栈中;
b、转移。
7、call 指令不能实现短转移,call指令至少实现近转移或者段间转移,除此之外,call指令实现转移的方法和jmp 指令的原理相同。
8、call 标号,段内近转移,将当前的IP 压栈后,转到标号处执行指令,CPU执行此种格式的call指令时,进行如下的操作:
a、压栈,相当于" push IP ":(sp) = (sp) –2
((ss)*16+(sp)) = (IP)
b、修改IP的值,相当于"jmp near ptr 标号":(IP) = (IP) + 由16个bit位所表示的位移,其中位移的计算方式为:“标号”处的地址-call指令后的第一个字节的地址,地址范围是-32768~32767,位移由编译程序在编译时算出。
9、指令“call far ptr 标号”实现的是段间转移。CPU执行“call far ptr 标号”这种格式的call指令时的操作:
a、执行“push CS、push IP”,将CS和IP压栈:
(sp) = (sp) –2
((ss) ×16+(sp)) = (CS)
(sp) = (sp) –2
((ss) ×16+(sp)) = (IP)
b、执行“jmp far ptr 标号”,修改CS和IP的值,(CS) = 标号所在的段地址,(IP) = 标号所在的偏移地址。
10、call 16位寄存器:表示转移到16位寄存器所指示的地址执行,实现段近短转移。
a、执行“push IP”,将IP压栈:
(sp) = (sp) –2
((ss)*16+(sp)) = (IP)
b、执行“jmp 16位寄存器”,(IP) = (16位寄存器)。
11、call word prt 内存单元地址:表示跳转到内存单元执行。
a、执行"push IP",将IP压栈
b、执行“jmp word prt 内存单元地址”,修改IP的值,跳转到内存单元地址处执行。
12、call dword ptr 内存单元地址:表示跳转到内存单元执行。
a、执行"push CS"和"push IP",将CS和IP压栈。
b、执行"jmp dword ptr 内存单元地址",修改CS和IP,跳转到指定处执行。
13、call指令转去执行子程序之前,call指令后面的指令的地址将存储在栈中,所以可以在子程序的后面使用ret 指令,用栈中的数据设置IP的值,从而转到call指令后面的代码处继续执行。
14、mul,乘法命令。相乘的两个数:要么都是8位,要么都是16位。
a、如果都是8位:一个默认放到AL中,另一个放在8位寄存器或者内存的字节单元中,此时的结果存放到AX中。
b、如果都是16位:一个默认放到AX中,另一个放在16位寄存器或者内存的字单元中,此时的结果,高16位存放到DX中,低16位存放到AX中。
15、mul的调用格式有两种:"mul register"或者"mul 内存单元"。
16、div是除法指令,使用div作除法的时候:
a、除数:是8位或16位,在寄存器或内存单元中
b、被除数:当除数为8位的时候,被除数默认放在AX,当除数为16位的时候,被除数默认放在DX和AX中。
c、结果:为8位或16位。当除数为8位的时候,商存放于AL中,余数存放于AH中。当除数为16位的时候,商存放于AX中,余数存放于DX中。