第一章:
1:1.2中讲的“计算机中的数制”,微机原理老师上课没有讲,不过她说过我们在数电课就上过,结合上学期所学的,我们应该了解二进制、十进制、十六进制、八进制之间的转换,不过估计程度不大,如果考试要考查相关的知识点的话,应该只会出填空或选择或判断题之类。
2:1.2.2中讲得计算机中常用的编码,这个当中的BCD码,鉴于第二章中的一些指令(例如AAA)的实现和这个有关系,建议关注一下,掌握如何写出某一十进制数对应的BCD码。至于BCD码的加法与减法,了解一下。对于字符编码,由于在计算机编程当中用的较多的编码就是属于字符编码的ASCII码,这里只需了解一下即可。汉字的编码同样了解即可。
3:1.2.3当中讲到的是计算机中带符号数的表示方法。其中要知道对于有符号数,其最高位,也就是我们一般用8位二进制表示的数中最左边的那一位用来表示正负而不是大小。在这里必须强调一点,那就是要掌握计算机机器数的三种不同编码形式(原码,反码,补码),其中,对于正数来说,这三种编码的结果是一样的,具体来说就是:比如整数12,其原码是:00001100,反码也是00001100,补码也是00001100;但对于负数来说,就不一样了,我们在后面的课程当中遇到的负数都是以其补码的形式出现的,例如第二次补充作业当中过的数8FH,这个数把它转换为二进制,就是:10001111,对于这个数来说,如果它表示的是一个有符号数,那么一定是负数(也就是说,
如果一个数已经确定是有符号数,只要它的最高位(用十六进制表示的话)大于等于8,就表示它是一个负数。))因为对于一个负数来说,从原码到补码的转换过程中,最高位也就是符号位是绝对不会发生改变的,具体的转化方法请仔细阅读书本的P10与P11。(在这里我先具体解释一下,根据原理,对于一个负数,把它的补码再求一次补码即得到其原码,我们来试一下:1000 1111先求它的反码,得1111 0000,然后加上1,得1111 0001,也就是十进制数-71。)
4:关于数的小数点表示法这一小节内容,老师压根就没提过,所以老规矩,了解一下看一下就可以了。
5:1.3节中,有一些基本概念,老师上课时说过会考一些填空题或者是判断题,我列举一些我有些许印象的知识点吧,其它的你也可以自己看看别人上课做的笔记,看有木有漏掉的!:
微型计算机的组成:硬件和软件
硬件:主机、输入输出设备、电源三部分。
一般的微机由微处理器、总线、存储器、输入/输出设备以及各种接口模块等组成。
一个微处理器主要包括运算器、控制器和寄存器组。
运算器又称逻辑运算单元(ALU)(运算器的英文一定要记住啊,说不定填空就考啊!)。
控制器主要是由程序计数器PC、指令寄存器IR、指令译码器ID和控制逻辑PLA等部件组成。(同上,英文不要忘记哈!)
系统总线有三个,分别是地址总线(AB),数据总线(DB),控制总
线(CB),其中,有一点是尤为要记住的,就是:微处理器数据总线的条数决定CPU和存储器或I/O设备一次能交换数据的位数,是区分微处理器是多少位的依据。比如我们说的64位CPU,就是意味着这个CPU的数据总线是64位。
软件:一般可分为系统软件和用户软件。
6:1.4节中微型计算机的工作过程,怎么说呢,老师上课是讲了很久,但个人觉得这一知识点应该不会成为考试重点,推荐你还是了解一下吧。
接下来的重点就是第二、三章了,我会把那些重要的指令的具体用法用举例子的方法来详细说明:
1:首先,我们得稍微了解一下8086/8088的内部结构,首先我们得知道,它是由两个独立的处理部件组成的:执行部件EU和接口部件BIU(老规矩别忘了英文啊!!!)
其中你要记住,通用寄存器组(就是那些我们用到除了段寄存器以外的寄存器)和程序状态寄存器FR是位于EU里面的,而段寄存器则是位于BIU里面的,为了加深理解,我这样说:那些段寄存器是用来寻址的,而寻址是从存储器那里寻的,那里也可以说是接口,这也就对应了BIU中的I(接口)。其他的寄存器是用来计算之类的,对应的就是EU中的E(执行)了。
2:指针寄存器SP和BP,你需要记住它们分别是和哪个段寄存器连用的。变址寄存器DI和SI,记住他们一般用于寻址中。
3:关于程序状态寄存器FR,你要知道:状态标志位有CF、ZF、SF、PF、OF、AF,而控制标志位有DF、IF、TF,其中,在第三章中出现频率较多的有CF,Zf,SF,PF,OF,你看看。
4:至于第二章后面将的具体工作电路之类的,你看一下,了解了解即可。
5:还有一点,一些段寄存器的常见搭配,如下:
CS:IP;DS:(SI、BX、DI);SS:SP;ES:(SI,BX,DI)
第三章:
1:寻址方式,这个必须要掌握:
先说最常见的三种,分别是立即数寻址、寄存器寻址、存储器寻址(端口地址在讲IN与OUT指令时会单独讲解)下面给出具体解释。
立即数寻址:由字面可以看出,它是用一具体的数字,一般是8位或16位,也就是2位十六进制数与4位十六进制数来表示要操作的地址,例如:MOV AX,5678H(表示从地址为5678H的内存单元中取出值来给AX,具体MOV指令的用法稍后会讲到)
又例如:MOV AX,56H(表示从地址为56H的内存单元中取出值来给AX)
关于立即数寻址的一些注意事项:立即数只能作为源操作数,决不能做目的操作数(源操作数,一般来说,就是指令中逗号右边的那个,目的操作数就是逗号左边的那个)
寄存器寻址:在这种寻址方式中,具体存放值的地址我们是不知道的
(好几本教材都没透露那些寄存器的地址),值是存放在寄存器中的,我们在使用时直接用寄存器的名字代表即可,例如:
MOV AX,BX(意思就是说把存放在BX中的值传送给AX)
除了AX,BX,CX,DX等这些寄存器值外,别忘了还有那些诸如DI、SI之类的寄存器以及AL、BL这些8位寄存器,它们也算是寄存器寻址,例如:MOV AX,DI(意思就是说把存放在DI中的值传送给AX)存储器寻址:这也是所有三种寻址方式中最难,最麻烦的一种,它有好多分类:
直接寻址:与立即数寻址很类似,都很直接。不过直接寻址最大的不同的方面在于,它给出的是偏移地址,具体的物理地址必须自己去算出来,而立即数寻址则是直接给出具体的物理地址。例如:
MOV AX,[2000H](意思就是说,从偏移地址为2000H,段地址为DS 的内存单元中拿出存储的值传给AX)
注:如果直接是这种格式:[某一具体的数],则默认是在数据段DS中寄存器间接寻址:看上去也与寄存器寻址很类似,其实还是有很大的区别的。寄存器间接寻址是指:在某一寄存器中存放的是一偏移地址,例如:MOV BX,2000H
MOV AX,[BX](把两行的指令连接在一起就知道,最终执行后把偏移地址为2000H,段地址为DS的内存单元中的值传给AX)寄存器相对寻址:还记得上一章中的那四个寄存器BX,SI,DI,BP 它们就是干这个活的,他们中的某一个与一具体的数加在一起共同表示偏移地址,但要注意一些默认的搭配,如下:
对于BX、SI、DI寄存器来说,段寄存器如果没有具体给出,则默认是数据段(DS)寄存器;对于BP寄存器来说,默认是堆栈段(SS)寄存器。如果前面给出了具体的段,例如:(ES:[12H+BP],那这里的段地址只能是ES了,这就是段超越)
基址加变址寻址:这里的偏移地址是由两个寄存器——一个基址寄存器(BX和BP)和一个变址寄存器(SI和DI)的和构成。
例如:MOVAX,[BX][SI]
注意事项:决不允许把两个基址寄存器或两个变址寄存器合在一起,例如不允许:MOV AX,[BX][BP]
相对基址加变址寻址:就是在基址加变址寻址的基础上多了一个具体的数,它有很多等同的写法,举几个例子:
MOVAX,20H[SI][BX]
MOVAX[20H+SI+BX]
还有一个隐藏寻址,了解一下就可以,具体的等学到相关指令时在详解。
(终于要开始讲解指令了,先列出暂时要详解的指令:MOV,XCHG,(PUSH,POP),(IN,OUT),(LEA,LDS,LES),(LAHF,SAHF,POPF),(ADD,ADC,INC),(SUB,SBB,DEC),(MUL,IMUL),(DIV,IDIV),(AAA,AAS,DAA,DAS ,AAM,AAD),(CBW,CWD),(AND,OR,XOR,TEST,NOT),(SHL/SAL,SHR/SAR),XLAT
(一个要注意的,如果表示的数的首位是字母的话,也就是从A
到F,必须在前面加上一个0,例如:MOV AL,0F2H或者是MOV AX,0FFFFH)
(一些简单或不经常使用的指令我就一点而过,具体的你就自己在书上看,主要是那些使用频率很高的那些指令)
MOV指令:
格式:(寄存器——寄存器),(寄存器——内存单元),(内存单元,寄存器)。决不允许两个操作数同时为内存单元。
一些注意事项:
●源操作数和目的操作数类型必须匹配,不能类型不一致,例如:
MOV AX,BL(AX为16为数,BL为8位数,不允许)
●段寄存器不能被直接通过MOV指令赋值,必须通过其他的寄存器
间接赋值,例如,不允许这样:MOV DS,0而只能先MOV AX,0 然后再MOV DS,AX
●代码段寄存器CS和指令寄存器IP不能作为目的操作数,但CS可
以作为源操作数。
●MOV指令对程序状态寄存器FR没有任何影响
●段寄存器之间不能相互赋值,例如,不允许:MOV DS,ES
关于诸如此类格式,例如:MOV AX,[1000H]和MOV AL,[1000H],前者是传递连续的两个内存单元的值赋给AX,且高地址内存(此处是[1001H])的值传给AH(即AX的高8位),低地址内存(此处是[1000H])赋给AL(即AX的低8位),此时寄存器的位数告诉了我们到底传递的是字单元还是字节单元。如果没有寄存器来给
出。
还有关于类型说明符,例如:
MOV PTR WORD:字型
MOV BYTE WORD:字节型
XCHG指令:
格式:(寄存器,存储器)、(存储器,寄存器)、(寄存器,寄存器),千万不能是(存储器,存储器)
注意事项:段寄存器(CS,SS,ES,DS)不能参与交换
接下来又是一个重点,那就是堆栈操作系列的指令,首先必须要掌握堆栈这一特殊存储方式的基本特点,即先进后出或后进先出,还有一个,那就是无论是POP指令,还是PUSH指令,它们都得操作数必须是16位(一个字),另外,它们对程序状态寄存器FR均没有任何影响。
PUSH指令
格式:指令后只跟一个操作数,可以是16位寄存器,或者是[具体的数字或是寄存器]这样的内存单元(此时涉及到的是相邻的两个内存单元,其中[]里面的最终表示的物理地址是低地址,例如:PUSH[2000H]表示的就是把内存单元[2001H]、[2000H]先后放入堆栈中;
PUSH AH 表示的就是分别把AH(AX中的高8位)、AL(AX
中的低8位)放入堆栈中
关于堆栈的具体过程:在执行PUSH指令之前,要设置好堆栈段与SP指针,例如,我们要把段地址2000H设为堆栈段,21H设为栈底编码如下:
MOV AX,2000H
MOV SS,AX
MOV SP,21H
在执行PUSH指令时,先把SP指针的值减2,然后再把操作数压入栈中,例如,紧跟上面的编码,是: PUSH AX,则首先SP指向1FH,AH被压入SS:[20H],AL被压入SS:[1FH](注意这也就是为什么一般都把SP指针设置为一个奇数值,因为PUSH指令是每次操作一个字单元(就是两个字节单元),设为奇数的话可以正好到栈顶,即在堆栈段中偏移地址为0H的那一地址去,如果一开始把SP设为奇数的话,就不行了,会堆栈溢出,这一点是很危险的)POP指令:
与PUSH指令格式相同,但用途却是相反,其实,堆栈段就是一段暂时的数据保存段,等当下的工作搞定的时候,就可以从堆栈段中恢复原先的数据,POP的具体用法,我们紧跟PUSH中的编码,加上:
POP AX在执行POP指令前,SP指针指向的是1FH,则把SS:[SP+1],即SS:[20F]的值给AH,紧接着把SS:[SP],即SS:[1F]中的值给AL,最后,再让SP加2。上面的执行结果实际上把原先
AX的值恢复给了AX(这只是个例子,因为AX的值压根就没变化)一些注意事项:码段寄存器CS的值可以压入栈中,但绝不能从堆栈段中取出值来CS,即:可以这样PUSH CS 但不能这样POP CS
XLAT指令
比较简单的指令,你就记住它的实际作用,就是把偏移地址为(BX +AL)的内存单元的值赋给AL
IN指令:
格式:(INAX,某一八位具体数的地址,例如:
INAX,22H,注意这里没有中括号[],表示的就是从地址为22H与地址为23H的端口中取值分别给AL,AHINAL,某一八位具体数的地址,例如:
INAL,22H表示的就是从地址为22H的端口中取值给AL
INAX,DX,表示的就是从一16位地址的端口中取出值给AX,例如,如果DX先前为2222H,则表示的就是分别从地址为2222H与地址为2223H的端口中取值分别给AL,AH IN AL,DX 表示的就是从一16位地址的端口中取出值给AL,例如,如果DX先前为2222H,则表示的就是从地址为2222H 的端口中取值给AL)
总结:必须注意,在端口指令中,设计到寄存器的,必须是AL
或者是AX,其它的寄存器,包括是AH都不行的。
另外,如果端口是8位地址的,可以直接寻址,如果是16位地址的,必须要用DX寄存器(不能使用其它寄存器)存储地址后,再把DX放入指令。
OUT指令:
格式是把IN指令的源操作数和目的操作数颠倒过来就行了,用法与注意事项都与IN指令相同,这里就不讲了。
LEA指令:
格式:(寄存器,存储器),注意源操作数必须是存储器操作数
例如:LEA AX,[2000H],就是把2000H,也就是源操作数的偏移地址传给了AX
LDS指令:
格式同LEA指令是一样的,具体的用法请参考书本103面,这里只说些注意事项:它是把内存单元中的高地址字单元的值传给DS 作为段地址,把内存单元中的低地址单元的值传给指令中的寄存器作为偏移地址,例如:LDS DI,[2200H],假设执行指令前的段地址为3000H,则执行指令后就把字单元(32202H)的值,也就是分别把字节单元(32202H)、字节单元(32203H)存储的值传给DS,其中(32203H)的值作为DS中的高8位,而(32202H)的值作
为DS中的低8位。然后把字单元(32200H)的值传给操作数中的寄存器,其中(32201H)的值作为寄存器中的高8位的值,(32200H)的值作为寄存器中的低8位的值。
LES指令:
该指令与LDS指令几乎一模一样,唯一不同的在于它是把段地址传给ES段寄存器
四个标志位传送指令(在书本上的104面,老师讲得比较少,稍微看下吧,了解即可)
接下来又是重点的指令——运算指令
(关于运算指令的特别声明:任何的段寄存器都不能参与运算指令中,也就是说加减乘除四类运算指令中的操作数不能出现段寄存器)
ADD指令:
格式:(寄存器,存储器)、(存储器,寄存器)、(寄存器,寄存器),千万不能是(存储器,存储器),源操作数也可以是立即数
注意事项:两个操作数的位数必须一致。
插入的一点:在老师布置的作业中,经常可以看到当执行完一些指令后让同志们判断标志寄存器中某些位的变化情况,下面要对某些出现频率较高的标志位做出详细说明:
CF(进位标志位):当做加法时最高位出现进位,如让FFH与11H 相加,则毫无疑问最高位会出现进位,则此时CF=1;;当做减法时最高位出现借位,如让10H-22H时就会这样,此时CF=1。除了加法指令和减法指令之外,位移和循环指令也会影响进位标志,关于这两点,等碰到上述两个指令时再说。
PF(奇偶标志位):当结果的低8位中1的个数为偶数时,例如对于这样一个结果:F0H(1111 0000),则在这个数1的个数是4个,是偶数,则执行完相关的算术运算后PF=1。
ZF(零标志位):这个是最简单了,当运算后的结果为0时,ZF=1,否则ZF=0。
SF(符号标志位):这个要看运算后的结果,如果结果(此时把结果看作有符号数)为负数(即最高位为1,例如
10001111),则SF=1;如果结果为正数(即最高位为0,例如01111010),则SF=0;
AF(辅助加标志位):在做加法时,当位3需向位4进位时(例如11111111+11110100)或在做减法时,位3需向位4做借位(例如
11111011-11110100)时,AF=1,否则为0。AF多用于BCD调整指令,等碰到那些指令再细说。
再回到加法指令中来,接下来讲得是ADC指令:
格式:与ADD相同,只不过在结果中还要加入CF的值。
一般这个指令用于实现多字节加法运算。例如,实现32位的加法:
MOV AX,5678H
ADD AX,0BCDEH
MOV DX,1234H
ADC DX,789AH
第二行是让5678H与0BCDEH先加起来,如果有进位的话,就会让CF=1;然后第四行就是让1234H和789AH相加,并且还加上刚才出现的进位,也就是说最终实现的是12345678H与BCDE789A 的加法运算,并且把结果的高16位放在了DX中,低16位放在了AX中(之所以要这样做是因为8086CPU中最多的只有16为数据,是不能直接实现32位的加法运算的)
INC指令:
比较简单,就是让操作数的值自加1
SUB指令、SBB指令、DEC指令同对应的加法相关指令用法相同,可联系在一起记忆。
CMP指令:
与SUB指令只有一个不同点,那就是结果不会送回目的操作数。
MUL指令:
格式:只有一个操作数。不过另一个操作数必须是寄存器操作数
或是存储器操作数(不能是MUL 某一具体值)
首先看乘数是16位数还是8位数
乘数是8位数:首先要把一个操作数放在AL中(用MOV语句实现),然后在MUL另一个8位的操作数,结果是16位数,存储在AX中,其中AH存放高8位的结果,AL存放低8位的结果。
如果乘数是16位数:首先得把一个操作数放在AX中,然后在MUL 另一个16位的操作数,结果是32位数,其中高16位存储在DX中,低16为存储在AX中
例如:MUL BYTE PTR[2222H](表示是两个8位数相乘(一个是AL中的值,另一个乘数是偏移地址为2222H的内存单元中存储的值,结果存放在AX中)
MUL WORD PTR[2222H](表示是两个16位数相乘(一个是AX 的值,另一个乘数是偏移地址为2222H的内存字单元中存储的值,结果的高16位存储在DX中,低16位存储在AX中)
MUL指令对标志位CF和OF有影响,但SF、AF、ZF和PF不确定
IMUL指令:
与MUL指令相同,只不过用于的是有符号数的运算,其中有一点要注意:
如乘积的高半部分(在AH或DX)中不是00或是0000,而是具体的数,则CF和OF置1,否则两个都置0,例如:
MOV AL,23H
MOV BL,01H
MUL BL(存放在AX中的值为0023H,则CF与OF均置0) DIV指令:
DIV指令对标致寄存器的影响情况不确定,一般不会考查
格式:与MUL指令一样也是只有一个操作数,不过另一个操作数必须是寄存器操作数或是存储器操作数(不能是DIV 某一具体值)如果是被除数是16位,除数是8位,则被除数必须在AX中(还是通过MOV语句实现),做完运算后,商放在AL中,余数放在AH中,例如:
MOV AX,5678H
MOV,BL,28H
DIV BL
如果是被除数是32位数,除数是16位,则被除数必须在DX与AX中存储(其中DX中存储的是高16位,AX中存储的是低16位),做完运算后,商在AX中,余数在DX中
(如果被除数与除数是相同的位数呢?DIV指令不允许被除数与除数位数相同,如果真的要做位数相同的除法,得用拓展指令拓展,例如要67H/33H,则要把67H拓展为0067H才能再用DIV指令,关于拓展指令稍后再将)
IDIV指令:
基本上与DIV指令相同,不过有一点要注意:余数的符号和被除数的符号相同