1. 为了以后的方便,我们定义了一个描述符号(),例(AX)表示急促器AX中的内容。
注意,()中的内容可以有三种 (寄存器名) (段寄存器名) (内存单元的物理地址)
2.我们约定,用idata表示常量,如mov ax [0],中的[0],就可以看做是形式[idata]
3 inc bx 是指bx 寄存器中的值需要加一。即自增指令
4.Loop语句的用法,它必然是一种有关循环的指令那个,一种经典的代码形式:
Mov cx,11
S: add ax,ax
Loop S 其中S是语句编号,S代表一种地址,这个地址是add ax,ax这条命令语句的首地址。
CPU执行loop s的时候,执行了两步操作:
一,(cx)=(cx)-1
二,检查(cx) 若等于零,则继续向下执行
若不等于零,则执行标号S处的指令语句。
5.如此可以总结循环功能的三个要点:
A CX里面放循环次数
B LOOP指令中标号所标识的地址要在前面
C 要循环指令的程序段要放在标号S和loop语句之间
6.规定:在汇编源程序中,数据不能以字母开头必须以数字开头,所以对于0至9FFF可以直接写,大于9FFF的数必须前边加0,即0A28DH。
7. 用debug命令来跟踪程序的运行,如果想一步就完成多步的运行,则可以用G命令。
g 0012 表示debug执行此命令后,CS:0012之前的代码段指令被一次性执行。
8.P命令除了可以进行执行跳转的指令即int 21之外,还可以用来一次性执行所有的循环,直至(CX)=0。而不用一步步地总用T来但不执行。
9.Debug和编译器 对于指令mov ax,[0]的理解和解释是不同的。Debug理解正常,但编译器会将其理解为mov ax,0
对此,我们可以用debug来试验。一是直接用A命令来写代码,二是编写汇编程序,用U命令来直接查看汇编命令或者编译连接为.exe 再用debug来追踪这个可执行文件 debug 1.exe.
10.为了应对第九题这样一个伟大的错误,我们在汇编编程的时候,可以用一下两个方法来解决:
一,用BX来作为立即数这样的偏移地址的载体,编译器是承认的。
mov ax,[bx] 编译器认为是访问内存单元而不是BX中的内容。
二.将段寄存器显化。mov ax,ds:[0]
11.我们如何将N个内存单元中的数据(八位)累加到一个16位寄存器中?有两种方法。
一是(dx)=(dx)+八位数据 错误:数据的位数不匹配
二是(dl)=(dl)+八位数据 缺陷:有可能造成数据不准确,因为会有进位产生超界。
目前解决此问题的方法就是借助一个中介:一个16位的通用寄存器
mov al,(内存单元)
mov ah,0
mov dx,ax 这样就解决了上述两个问题。
12.对于变量X(比如访问一段连续内存时的偏移地址),我们可以用一个寄存器来充当。递增的话可以用 格式为 inc 充当变量的寄存器 pause120
13. 默认情况下,访问内存单元时,偏移地址就在BX中,所以有mov ax,[bx] 而此时数据的段地址就默认在dx中。
14.关于在实模式(纯DOS)下和保护模式下运行DOS的问题,实模式就是指没有任何保护措施下运行,也允许修改重要的内部信息。而保护模式下,当外界试图对重要信息所在段进行修改时,系统会阻止。出现如下提示:
15.我们是在操作系统下进行编程的,所以,对于一切我们可以利用的资源都是操纵系统给我们分配的,包括内存。而不应该自己任意指定内存写入等。因为我们并不知道重要的代码或数据在内存的什么位置。我们必须学会使用操作系统为我们分配的内存。
但是,我们也应该知道,我们用的是汇编语言,应该尽量去实现直接操作硬件进行编程的理想。
现在的操作系统已经发展的很发达,将CPU保护模式所提供的功能都已经严格管理了。
所以,我们必须需要一段完全安全的内存单元,供给我们进行编程。这段就是0:200h-0:300h
这256个字节长度的内存,我们约定,这段内存不存放系统重要的代码或数据。可以用D命令查看,如果内存中的内容都是0,则我们便可以对这段内存进行任意操作。
16.表示段超越前缀的用处:当从一段内存复制内容到另一段内存时进行复制操纵时,当两段内存的起始地址相差超过64KB时。。。 参考P124
17.mov 指令支持从内存单元到内存单元的移动吗?在源程序中,我们看到的的当数据从一个内存单元到另一个内存单元移动时,是借助了中介:寄存器。
18.add ax,cs:[bx]这条指令可以在你不知道段地址的情况下,将当前的指令复制在目的内存中。
19 dw是什么意思?是定义字型数据的意思,即define word
20.我们知道可执行文件包括程序以及描述信息。程序来自于源程序中的汇编指令和所定义的数据,描述信息来自于编译、连接程序对源程序中伪指令等相关信息的处理。
关于end指令,它不仅通知了编译的结束也提供了程序的入口信息。参考P128
这样的信息存在于.exe文件中,这样CPU根据这样的信息来设置CS:IP等
对于汇编程序,我们一般的正确格式就是用代码段用start 后面用end start此时,代码段、数据段和栈段这三个段的位置我们可以任意放置。
如果没有start的话,只有当把代码段放在最前面时,程序才可以正确执行。
21.我们说如果源程序中定义了N个字型数据,dw 1h,2h,3h,4h,…….
也表明是开辟了N个字型内存单元,这段空间内的数据依次是1h,2h,3h,4h,…
这两种结果是一样的。
22.第一个比较像样的汇编程序P131,它是将代码、数据和代码放在了各自不同的段中,由于段名表示的是此段的段地址,所以程序中出现了 mov ax,段名 这种格式。
在编写汇编程序时,在代码段开始的时候,我们是应该做好初始化工作的包括对数据段、栈段、代码段等的初始化。即将各段的标号赋值给相应的段寄存器。此时CPU才知道了各自段的对应关系。
并且在此时,比如对于数据段我们使数据段标号data,不能直接这样写mov ds,data
因为编译器将data解释为立即数,而8086是不允许将立即数直接赋值给段寄存器的。而应该这样 mov ax,data mov ds,ax…
23.and指令作用就是 使得某些特定位归零,其他位保持原来的数据。
or 指令作用就是 使得某些特定位归一,其他位保持原来的数值。
24 C语言中,字符是单引号,字符串是双引号。’a’ “unix”
但汇编语言中字符和字符串都是单引号 mov ax,’a’ db ‘unix’
25.汇编语言中的字母的大小写:x与X的区别之处就是ASCII码,小写的第五位是1,大写的第五位是0。所以进行大小写转换时,我们就可以用and or指令 p143
26 P145揭示了汇编语言和C语言的某种相通之处。
就是表示数组的方式 C中 a[4]
汇编中 2[si] idata[bx]
27.在程序中我们是可以通过16位寄存器来作为中介来进行内存之间数据的传送的。也 就是说命令mov ax,[bx] mov ax,[si] mov ax,[di] 等都是成立且正确的,他们表示传送的是字单元,即一次传送两个字节。
至此,我们也可以知道这样一点:8086不支持内存单元间数据的直接传送。
28 bx是通用寄存器,可以作为变址寄存器。
以下两个跟BX的作用类似: Si源变址寄存器 和 di 目的变址寄存器
但是si di均不能拆开为两个8位的寄存器来使用。 Pause 150
29.关于div指令
除数为8位或16位,存于一个寄存器或一个内存单元中。不能是立即数。
被除数16位或32位,默认存在于ax或dx ax中(此时dx中存高16位,ax存低16位)
结果存于 商在al(16/8)余数在ah或商在ax(32/16)余数在dx。
格式为 div reg
div 内存单元 div byte ptr bx
30.定义数据的伪指令 db(define byte) dw(define word) dd(define double word)
dup是一个操作符同上述三个一样都是被编译器识别的符号。它和上述三个配合使用用来重复定义相同类型的数据。
格式 数据类型 重复次数 dup(循环体)
db 1000 dup(0) dw 220 dup(0FFFFH) db 10 dup(1,2,3) db 10 dup(`abc`,`ABC`)
31.转移指令的概念:可以修改IP或可以同时修改CS和IP的指令。即可以控制CPU执行
某处内存中代码的指令。
8086CPU的转移行为分为:段内转移(只修改IP jmp ax) 段间转移(同时修改cs ip jmp 1000:0)
其中段内转移又可以分为 短转移(IP变化范围-128——127) 近转移(-32768——32767)
8086的转移指令有以下几类:无条件转移指令(jmp) 条件转移指令 循环指令(loop) 过程 中断 以上五类转移指令虽然前提可能是不同的,但其转移原理是类似的。此章我们着重学习jmp来深入理解转移指令原理。
32.Offset在汇编语言中是由编译器识别的符号,功能是获得标号的偏移地址。
这里的标号可以指语句标号也可以是任何标号。
例 mov ax,offset s
33.jmp指令 即无条件转移指令。
Jmp short 标号 (转移到标号处执行指令)此处标号为代码段中的标号。
见P176 177,CPU在执行jmp指令的时候并不需要转移的目的地址。
我们知道,我们编写的汇编源程序、debug中的汇编代码、机器码 这三者跟CPU的亲近程度是不同的,机器码最贴近CPU。我们通过jmp指令及其对应的机器码可知:当目的地址不同而所转移的指令相同时,机器码是一样的。这说明cpu在执行jmp指令的时候是不需要转移的目的地址的。
但CPU不是神仙,它是如何转移到目的指令的呢?见P178
在 jmp short 标号 这条指令的机器码,并不包含转移指令的目的地址,而是包含了IP所要转移的位移。其中这个位移是编译器根据程序中的标号计算出来的。显然这个位移是在汇编编译的时候的到的。
由此便可以得到转移指令真正的功能:jmp short s (ip)=(ip)+8位位移 段内短转移
Jmp near ptr s (ip)=(ip)+16位位移 段内近转移
34jmp far ptr 标号 为段间转移,又称远转移。
Far ptr在这里也就指明了指令用标号段地址和偏移地址来修改CS和IP。
远转移同短转移和近转移不同之处就很明显了:因为它是段间转移所以CS也是要修改的,所以它的机器码中装的不是所要变化的位移,而是真正就是转移指令的目的地址,包括段地址和偏移地址。其中偏移在高位内存单元中,段在高单位。
35.jmp指令后面不仅可以跟代码段中的标号,可以使执行指令的入口迅速转移到标号处,还可以后跟一个寄存器,是IP的值改为16位寄存器中的值,实现段内转移。
同样,Jmp后面还可以跟内存单元,此时既可以实现段内转移也可以实现段间转移。
A jmp word ptr ds:[bx] (ip)=字单元中的16位位移量
B jmp dword ptr ds:[bx] (ip)=字型内存单元中内容 (CS)=(内存单元地址+2)
36从P181的检测点9.1来看,我们可以总结:我们可以用jmp指令来控制指令的转移。无论目的指令前面有没有标号,有就好说了,
没有的话就用例题所用的方法,找到它的地址,若是段内转移直接用寄存器或字型的内存单元即可;若是段间转移就借助一个双字的内存单元。
37、jcxz指令为条件转移指令,所有的条件转移指令都是短转移。所以在对应的机器码中都是要转移的位移,而不是转移指令的目的地址。且IP位移范围-128——127用补码表示。
指令格式jcxz 标号 (如果(cx)=0,则转至标号处执行指令) jmp if cx=zero
8位位移在编译时得出,算法为 标号处指令地址sub jcxz指令后第一个字节的地址。
如果cx的值非零,则什么操作也不做 即 if((cx)==0) jmp short 标号
38.loop指令是循环指令,循环指令都是短转移。 格式:loop s (先做cx-- 若非零则跳转)
小总结:cx=0跳转:jcxz cx=!0跳转:loop
39.浅谈根据位移进行转移的意义:对于段内转移,机器码中没有转移指令的目的地址而是偏移地址,这样做有利于代码段在内存中浮动分配。
40