《汇编语言》第四版笔记
第一章 基础知识
知识点
-
总线宽度:该总线有N个根线,则可以说这个总线的宽度为N
-
主板:主板上有一些重要器件,器件通过总线连接。这些器件有CPU,存储器,外围芯片组,拓展插槽等。拓展插槽一般插有RAM内存条和各类接口卡
-
接口卡:CPU通过总线向接口卡发送命令,接口卡根据CPU命令对外设进行控制
-
从存储器的功能和连接上,可以分为以下几类
- 随机存储器RAM:
主随机存储器一般由两个位置上的RAM组成:装在主板上的RAM和插在拓展槽的RAM - 装有BIOS(基本输入输出系统)的ROM:
BIOS是由主板和各类接口卡的厂商提供的软件系统,可以通过它对硬件系统进行基本输入输出 - 接口卡上的RAM:
某些接口卡需要存储大批量数据,上面就装有RAM,比如显卡里的显存
- 随机存储器RAM:
-
CPU把所有的存储器看作一个假想的逻辑存储器(又称内存地址空间)去使用
-
内存地址空间的大小受CPU地址总线宽度的限制
第二章 寄存器
知识点
- 通用寄存器:用来存放一般数据的寄存器,如AX,BX,CX,DX
- 因为以前的寄存器都是八位的,为了保证兼容,8086的16位寄存器可以拆分成两个独立的寄存器单独使用。如:AX可以拆分为AH和AL
- 因此8086CPU的寄存器可以一次处理两种尺寸的数据:字节(8位)和字(16位)
- 对于汇编指令 add al,93H
al是当作一个单独的寄存器来使用的,如果结果溢出,高位并不会放入ah寄存器 - 一个合法的汇编指令,指令的两个操作对象位数应该是一致的,以下指令不合法:
mov ax,bl (8位寄存器和16位寄存器之间传数据
mov al,20000 (8位数据存不了20000 - 8086CPU是16位的,但是地址总线是20位的,不能让这16位字长限制了寻址范围,因此8086CPU采用两个16位地址合成一个20位物理地址的方法
- 两个16位地址一个是段地址,另一个是偏移地址,通过一个加法器算出物理地址
- 地址计算公式:段地址*16+偏移地址=物理地址
含义:用一个基础地址(段地址*16(乘16在十六进制里就是左移一位))和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址
既然是通过相对的关系得出物理地址,所以一个物理地址可以有多种表示形式 - CPU可以将若干连续的内存单元看作一个段。段的大小受机器字长影响,8086CPU为16位,因此一个段的大小不能超过64KB
- 段地址由CPU中的段寄存器提供,CS就是其中一个段寄存器
- CS(存指令段地址)和IP(存指令偏移地址)是8086CPU两个关键的寄存器
任意时刻,设CS的内容为M,IP内容为N,CPU将从内存:M*16+N单元开始读取指令并执行 - 正常来说,CPU更新IP的时机:取出指令后,在指令执行之前
- 同时修改CS,IP:jump 段地址:偏移地址(用指令中的段地址修改CS,偏移地址修改IP)
- 仅修改IP,JUMP 寄存器 (用寄存器中的值修改IP)
预备知识:debug的使用
- R命令查看,改变CPU寄存器的内容
- 改变寄存器内容:r命令+寄存器
- D命令查看内存中的内容
- 列出从指定内存单元开始的128个内存单元的内容:d 段地址:偏移地址
- 在使用“d 段地址:偏移地址”之后,用d命令查看后续内存内容
- 指定内存查看范围:d 段地址:起始偏移地址 结尾偏移地址
- 列出的内容分为3部分:左边是地址,中间是内容,右边是各内存单元中的数据对应的ASCII码,如果没有对应的,用'.'代替
- E命令改写内存中的内容
- 连续修改内存内容:e 起始地址 数据 数据...
- 采用提问方式逐个改写内容:
1.e 段地址:偏移地址,按enter键
2.输入新数据+空格/空格
3.希望改写的单元改写完毕后,按enter键 - e命令可以写入数据,字符,字符串
- U命令将内存中的机器码翻译成汇编指令
- T命令执行一条机器指令
- A命令以汇编的形式在内存中写入指令
第三章寄存器
知识点
- 字单元:存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址同理
- 起始地址为N的字单元简称为N地址字单元
- 8086CPU有一个DS寄存器,用来存放数据的段地址(CS存指令的段地址)
- 指令格式:mov 寄存器 [内存单元地址],[]里面的是偏移地址,段地址此时从DS获取
栈(低地址存低位
- CPU提供相关指令以栈的形式访问内存空间,这意味着可以将一段内存当作栈来使用
- 8086CPU的入栈出栈操作都是以字为单位进行的
- 8086CPU中,段寄存器SS存栈顶的段地址,寄存器SP存偏移地址。pop和push指令执行的时候,CPU从这两个寄存器获得栈顶地址
- 栈的起始位置从底下开始,空栈时,栈顶指针指向栈的最底部单元的下一个单元
- push和pop指令可以只给出内存单元的偏移地址,其余信息由DS,SS,SP寄存器给出
- push和pop本质上就是内存传送指令,和move指令不同的是。move指令只需传送数据,而push和pop除此之外还要对栈顶指针操作
- push先改变栈顶指针,再传输数据。pop先传输数据,再改变栈顶指针
- 一个栈段最大容量为64KB(因为偏移地址就4位十六进制)
段的综述
可以将一段内存定义为段,段地址指示段,偏移地址访问段内单元。
数据段 | 代码段 | 栈段 |
---|---|---|
DS:偏移地址 | CS:IP | SS:SP |
CPU将某段内存当作代码,是因为CS:IP指向了那里;CPU将某段内存当作栈,是因为SS:SP指向了那里
预备知识:debug的使用
- debug指令可以通过段寄存器给出段地址 如:d 段寄存器:偏移地址 (e,a,u指令同理)
- debug的T命令在执行修改寄存器SS的指令时,下一条指令紧接着被执行(即按一次T执行两条指令),原因跟中断有关
第四章 第一个程序
知识点
-
可执行文件包含两部分内容:程序和数据,相关描述信息
-
操作系统依照可执文件中的描述信息,将可执行文件中的机器码和数据加载入内存,并进行相关初始化,(如设置CS:IP),然后由CPU执行程序
-
在汇编语言源程序中,包含两种指令:汇编指令和伪指令
-
伪指令:编译器执行的指令,编译器根据伪指令进行相关编译工作.下面介绍三种常见的伪指令
- segment和ends:这对伪指令的功能是定义段。一个段必须由一个名称标识。标识名称在编译,连接后处理为一个段的地址
- end:和上面的ends不同,end是一个汇编程序的结束标记,编译器遇到后结束对文件的编译
- assume 段寄存器:标识符:假设某一段寄存器与程序中某一个段相关联
-
一个汇编程序由多个段组成,源程序中的指令,数据,栈被划分到了不同的段中
-
用masm和link进行简化的手动编译和连接
1."masm"后面+"源文件路径文件名;",按enter即可编译(不用加文件后缀名
2."link"后面+"目标文件路径文件名;",按enter即可连接(不用加文件后缀名 -
连接的作用:源程序很大时,可以分为多个源程序来编译,编译后由连接程序将它们连接到一起
-
任何操作系统,都有一个shell(外壳)程序,用户通过这个程序操作计算机系统
command.com是dos系统的shell, command执行用户输入的命令,将用户输入的程序加载入内存,设置CS:IP后,command停止运行,CPU运行程序 -
debug可以跟踪程序(如debug 1.exe):debug将程序加载入内存,设置CS:IP后,debug不放弃对CPU的控制,这样可以执行相关命令来单步执行程序
单步执行的时候,执行INT指令的时候用P命令,其余用T
command加载debug,debug加载要运行的程序,返回顺序相反 -
DOS系统中.exe文件中的程序所占内存分为两部分:PSP(称为程序前缀的数据区,DOS利用PSP和程序进行通信)和程序本身。其中PSP占前256字节。
假设程序所在内存区的段地址是SA,则SA:0到SA+10H:0存PSP,其余存程序,程序段地址SA存在DS寄存器中
第五章 [BX]和loop指令
知识点
- “()”表示一个寄存器或者一个内存单元中的内容
- idata表示常量
- inc bx的含义是bx中的内容+1
loop指令
-
CPU执行loop指令的时候,要进行两步操作
1.(cx)=(cx)-1
2.判断cx中的值,如果不为零则转至标号处执行程序,为零则向下执行 -
loop实现循环要点
1.在cx中存放循环次数
2.loop指令的标号所表示的地址要在前面
3.要循环执行的程序段,要写在标号和loop指令的中间 -
用loop实现循环的框架
点击查看代码
mov cx,循环次数 s: 循环执行的程序段 loop s
-
在汇编语言源程序中,数据不能以字母开头,所以要在前面+0
-
在debug的时候,可以通过g指令跳过一些代码,起到断点的作用
如:-g 0112 表示执行debug从当前CS:IP开始执行,到IP的值为0012为止 -
当执行处于循环的代码时,使用p指令可以跳出循环
debug和汇编编译器masm对指令的不同处理
-
现在编写汇编语言源程序有两种方法:
1.在debug里用A指令写到内存中 2.在masm里面编写
不过两种方法对于类似于 mov al,[0]的指令有不同的解释。
- debug将[]里面的0看作内存地址,将偏移地址为0的内容写入al
- 而masm编译器将[0]看作0,将0写入al
masm可以在[]前面加上"ds:",显式告诉编译器访问内存,段地址在ds中。如: mov al,ds:[0]
-
这些出现在指令中,用于显式指明内存单元段地址的"ds:",在汇编语言中称为段前缀。(只有段寄存器才是段前缀!常用段寄存器:ds,cs,ss,es
- 前面几章在debug编写源程序时,都是直接在某个内存中写入汇编指令的,但这很有可能修改重要的系统数据或代码,是不严谨的
dos方式下,一般,0:200~0:2ff空间是没有数据和代码的,以后利用这段内存写入内容
第六章 包含多个段的程序
知识点
-
上一章提到:不能随便更改内存内容。只有向操作系统申请取得的空间才是安全的。程序取得空间的方法有两种:
- 加载程序时为程序分配。我们通过在源程序中定义段来进行内存空间的获取
- 在程序执行时向系统申请
-
在程序中,我们可以定义我们需要的数据,数据被编译连接后将成为可执行程序的一部分。
可以用指令"dw 0123h,0789h"定义字型数据:define word,此时一个数据两字节 -
dw定义的数据,存放在程序地址的开头。存完这些数据之后才存汇编指令。
-
如果开头dw定义了数据,那么内存地址的最开始不是我们要执行的指令,因此需要指出程序的真正入口(第一条汇编指令的地址),继而更改我们的CS:IP
如下代码,end除了通知编译器程序结束之外,还可以通知编译器程序的入口在哪个地方。 在编译连接后,end start指明的程序入口被转化为入口地址,存储在可执行文件的描述信息中(可执行文件由描述信息和程序组成)
点击查看代码
assume cs:code code segment 数据 start: 代码 code ends end start
-
因此CS:IP变化过程为:程序加载后,由DS寄存器得到程序所在内存区的段地址,CS=DS+10H,IP偏移地址由可执行文件的描述信息指出,最终CS:IP指向第一条汇编指令所在的内存
-
dw定义的数据占有一定的内存空间,因此可以dw一系列0数据,来得到一块内存空间
-
可以像定义代码段一样,通过segment和ends定义栈段,数据段,给个不同的段名就好
-
一个段名代表一个段地址 ,段名被编译器处理为一个段地址数值。可以有:mov ax,段名 mov ds,ax ,但不能:mov ds,段名,因为8086不允许数值直接赋值给段寄存器,其他cpu可以
第七章 更灵活的定位内存地址的方法
知识点
and和or
- and指令可以将位设0,or指令可以设1。想要设1或者设0,将对应位改成1或0即可。如:and al,01111111B即可将al的第七位设位0。or al,01000000B可以将第六位设为1
- 大小写字母的ASCII码的二进制表示,除了第五位,其他都一样,因此可以用and和or指令进行大小写转化
- 在汇编程序中,用'....'的方式指明数据是以字符的形式给出的,编译器将其转化为对应的ASCII码
- si和di是和bx功能相近的寄存器
- []里面可以放bx,si,di或者idata,均可以表示一个具体的内存单元的偏移地址
- 当寄存器不够用,需要暂存数据时,可以将其暂存到栈中。
第八章 数据处理的两个基本问题
知识点
- 寄存器汇总
- reg表示寄存器,sreg表示段寄存器
reg:ax,bx,cx,dx,sp,bp,di,si
sreg:ds,cs,ss,es
- reg表示寄存器,sreg表示段寄存器
- 只有四个寄存器可以出现在[]里面来寻址:bx,si,di,bp
- 这四个可以单独出现
- 也可以以特定组合出现:bx和si,bx和di,bp和si,bp和di。(si或di必须跟随bx和dp中的一个
- 只要[]里面有dp,若没有显式指出段寄存器,默认在ss中。(bx就默认段地址在ds中
寻址方式
- 直接寻址:[idata]
- 寄存器间接寻址:[bx],[si],[di],[dp]
- 寄存器相对寻址:[bx+idata],[si+idata],[di+idata],[dp+idata]
- 基址变址寻址:[bx+si],[bx+di],[dp+si],[dp+di]
- 相对基址变址寻址:[bx+si+idata],[bx+di+idata],[dp+si+idata],[dp+di+idata]
- 机器指令需要指出指令进行的是字操作还是字节操作,可以有三种方法指出
- 通过寄存器指出处理数据的尺寸
- 通过X ptr指明内存单元的长度,x可以是word或者byte。在没有寄存器参与的内存单元访问指令中,用X ptr显式指出要访问的长度是很有必要的
如:mov byte ptr [1000H],1和mov word ptr [1000H] - 有些指令默认了访问的是字单元还是字节单元,如push和pop
- 相对基址变址寻址[bx+si+idata]使得我们可以从结构化的角度看待要处理的数据,bx定位整个结构体,idata定位某个数据项,si定位数组项中的每个元素
除法指令div(需要用ptr指出要操作数据的大小
10/5=2,10是被除数,5是除数
- 除数:在reg或内存单元中
- 被除数:默认放在AX或者AX和DX中。如果除数为8位,被除数为16位,默认在AX中存放;如果除数为16位,被除数为32位,在DX和AX存放,DX放高位,AX放低位
- 结果:如果除数为8位,AL存商,AH存余数;如果除数为16位,AX存商,DX存余数
- 格式:div reg或者 div 内存单元
- dd定义双字型数据,一个数据四字节
- dup表示数据的重复:db 3 dup (0,1,2)定义了九字节,分别是0,1,2,0,1,2,0,1,2
第九章 转移指令的原理
知识点
- 可以修改CS或者IP的指令成为转移指令
- 转移指令分为
- 段内转移:用目的地偏移地址修改IP。比如:"jmp ax" 其中,段内转移有分为短转移和近转移
- 短转移:IP修改范围为-128-127
- 近转移:IP修改范围为-32768-32767
- 段间转移:用目的地段地址和偏移地址同时修改CS和IP,比如"jmp far ptr 标号","jmp 1000:0"
- 段内转移:用目的地偏移地址修改IP。比如:"jmp ax" 其中,段内转移有分为短转移和近转移
- 操作符offset:获取标号的偏移地址 如:"mov ax,offset start"表示将start第一条指令的偏移地址存入ax
- 一个转移指令需要指出目的地址,和转移的距离
- 转移指令jmp后面可以跟:标号,寄存器,内存单元
- jmp+寄存器在第二章已经见过了
- "jmp short 标号"表示的是段内短转移,"jmp naer ptr 标号"表示的是段内近转移
- 在一般的汇编指令中,idata立即数无论是表示数据还是内存单元的偏移地址,都会在机器指令中出现
- 不过以上两种形式,jmp机器码没有给出目的地的偏移地址,编译器会计算出从jmp的下一条指令到标号的位移,将位移放入机器码
- "jmp far ptr 标号"实现的是段间转移,机器码给出了目的地的段地址和偏移地址
- jmp+内存单元有两种格式
- "jmp word ptr 内存单元地址"(段内转移),内存单元地址处存放着一个字,是目的地的偏移地址
- "jmp dword ptr 内存单元地址"(段间转移),内存单元地址存放着两个字,高地址处的字是目的地的段地址,低地址处是目的地的偏移地址
- jcxc是条件转移指令,所以条件转移指令都是短转移,机器码中存放IP的位移
- 指令格式:"jcxz 标号(如果cx=0,跳转到标号,否则什么也不做,继续往下执行
- loop是循环指令,所以循环指令都是短转移,机器码存放IP的位移
- 指令格式:"loop 标号"(cx--,如果cx=0,跳转到标号,否则什么也不做,继续往下执行
- loop和jcxz两条指令跳转的条件正好相反!!!
- jmp short 标号,jmp near ptr 标号,jcxz 标号,loop标号机器码都是存放IP的相对位移,而不是实际的地址,这样程序在内存的不同位置都可以正确执行
- dec指令:和inc相反,dec bx表示bx--
关于jmp指令如何转化成机器码
- 向前转移:即标号s在loop s前面出现
- 计算位移,如果是-128-127,一律转变为jmp short s对应的机器码:EB disp(占两个字节);
- 如果是-32768-32767,对于jmp short s将产生编译错误,对于jmp near ptr s和jmp far ptr s分别产生对应的机器码,E9 disp(三字节),EA 偏移地址 段地址(五字节
- 向后位移,即loop s的时候标号s未出现,此时不能计算disp
- 对于jmp short s,生成EB和一个nop指令
- 对于jmp s和jmp near ptr s,生成EB和两个nop指令
- 对于jmp far ptr s,生成EB和四个nop指令
遇到标号s计算出disp时,再填充nop。如果disp属于-32768-32767,则对于jmp short s将产生编译错误
第十章 CALL和RET指令
知识点
-
"ret":利用栈中的数据修改IP的内容,等价于pop IP
-
"retf":利用栈中的数据修改CS和IP,等价于pop IP,pop CS
-
"call":将当前的IP或者CS和IP压入栈中,转移
- "call 标号":等价于push IP,jmp near ptr 标号
- "call far ptr 标号":段间转移,等价于push cs, push IP,jmp far ptr标号
- "call 16位reg":等价于push IP,jmp 16位reg
- "call word ptr 内存单元地址":等价于push IP,jmp wprd ptr内存单元地址
- "call dword ptr 内存单元地址":等价于push CS,push IP,jmp dword ptr 内存单元地址
-
可以用ret和call来实现调用子程序
格式为
标号: 指令 ret
mul指令
-
两个相乘的数
要么都是8位,要么都是16位- 如果是8位,一个默认在AL中,另一个放在内存或者8位reg
- 如果是16位,一个默认在AX中,另一个在内存或者16位reg
-
结果
- 如果是8位,默认放在AX中
- 如果是16位,高位在DX,低位在AX
子程序的标准框架
子程序开始:子程序中使用的寄存器入栈
子程序内容
子程序中使用的寄存器出栈;注意出栈入栈的顺序
返回(ret,retf)
第十一章 标志寄存器
知识点
- 标志寄存器有三种作用
- 存储相关指令的执行结果
- 用来为CPu执行相关指令提供行为依据
- 用来控制CPU的相关工作方式
- 标志寄存器flag存储的信息称为程序状态字
- falg每一位都有专门的含义
标志位
- ZF零标志位,相关指令执行结果为0,zf=1
- PF奇偶标志位,如果结果所有bit位中1的个数是偶数,pf=1
- SF符号标志位,如果结果为负,sf=1(对于有符号数才有意义
- CF无符号运算的进位标,如果加法结果对更高位有进位值或者减法向更高位借位,cf=1
- OF有符号运算的溢出标志位,如果发生溢出,of=1
- DF方向标志位:在串处理指令中,控制每次si,di的增减
- df=0,每次操作后si,di递增
- df=1,每次操作后si,di递减
指令
- adc带进位加法指令:"adc ax,bx"的功能是:(ax)=(ax)+(bx)+CF
- 加法可以分两步进行,低位相加,高位相加再加上低位的进位
- 因此,adc指令和add指令配合可以实现任意大的数据的加法
- add和inc的区别在于,后者不改变CF的值
- sbb带借位减法指令:"sbb ax,bx"的功能是:(ax)=(ax)-(bx)-CF
- 利用sbb指令可以对任意大的数据进行减法,原理和adc一样
- cmp比较指令:"cmp ax,bx"的功能是:计算ax-bx但并不保存结果,仅仅根据计算结果对标志寄存器进行设置
- 对于有符号数(关注zf,cf)
- zf判断ax,bx是否相等
- cf=1,说明ax<bx
- cf=0,说明ax≥bx
- cf=0并且zf=0,说明ax>bx
- zf=1或zf=1,说明ax≤bx
- 对于无符号数(关注of,zf,sf)
- 对于有符号数,如果溢出,sf的值判断结果正负是不正确的,因此还要关注of溢出位
- 如果of有溢出,那么结果的正负和此时的sf是相反的。如果没有溢出,结果正负和sf正负相同
- 对于有符号数(关注zf,cf)
基于cmp比较结果的条件转移指令(以下是无符号数
- je:等于则转移,zf=1
- jne:不等于则转移,zf=0
- jb:低于则转移,cf=1
- jnb:不低于则转移,cf=0
- ja:高于则转移,cf=0且zf=0
- jna:不高于则转移,cf=1或zf=1
- 其他操作也可能影响标志位,但是CPU并不关心逻辑含义,只要某位的条件满足,就发生转移
- movsb串传送指令:将ds:si指向的内存单元的字节送入es:di中,然后根据df的值,将si和di递增或递减
- movsw:将ds:si指向的内存单元的字送入es:di中,然后根据df的值,将si和di递增2或递减2
- rep:"rep movsb"等价于"s:movsb loop s",功能是根据cx的值,重复执行串传送指令,循环实现cx个字符的传送
- 使用串传送指令时,需要给出一些必要的信息
- 传送的原始位置ds:si
- 传送的目的位置es:di
- 传送的长度cx
- 传送的方向df
- cld:将falg的df设置为0
- std:将flag的df设置为1
- pushf和popf:将标志寄存器的值入栈or弹出数据,送入标志寄存器中
关于of和cf的判断
- CF是无符号数关注的,如果最高位出现借位或者进位,cf为1
- of是有符号数关注的,如果最高位和次高位异或为1(如果一个有进位,一个没有进位,就是溢出),代表溢出,of为1
第十二章内中断
知识点
- 中断:CPU不再向下执行指令,而是转而处理中断信息
- 中断处理程序:用来处理终端信息的程序,入口地址需要通过中断类型码获得
- 中断信息可以来自CPU内部或者外部
- 8086CPU有四个中断源
- 除法错误 类型码:0
- 单步执行 类型码:1
- 执行into指令 类型码:4
- 执行int指令 指令格式为int n,n就是类型码
- 不同的中断信息对应不同的处理,8086CPU用一字节的中断类型码区分不同的中断信息
- CPU通过类型码查中断向量表,从表中获取中断程序的入口地址
- 中断向量表放在内存中,对于8086,0000:0000到0000:03FF的1024单元放向量表,一个入口地址两字,高位地址放段地址,低位放偏移地址
- 中断过程:CPU硬件自动用中断类型码找到中断向量,并用它设置CS和IP。CPU收到中断信息后,首先引发中断过程,中断过程完成后,CS:IP指向程序入口,CPU再执行
中断过程的具体步骤- 从中断信息中取得中断类型码
- 标志寄存器入栈
- 将flag的第八位TF和第九位IF值设为0
- CS的内容入栈
- IP的内容入栈
- 找到入口地址,设置CS,IP
- 编写中断处理程序
- 保存要用到的寄存器
- 处理中断
- 恢复用到的寄存器
- 用iret指令返回
- 其中,iret指令等价于
- pop IP pop CS popf
- 编写中断程序的步骤
- 编写中断程序的代码
- 将中断程序送入内存(送到0000:0200到0000:02FF是向量表的一部分,但是一般没有那么多中断可以存
- 更改向量表存着的中断程序地址
- CPU执行完一个指令后,如果标志寄存器的TF位为1,则产生单步中断。进入中断处理程序。执行完中断处理程序的第一条指令,检测到TF为1,又产生单步中断进而造成死循环。所以中断过程需要将TF设为0
- 有时候发生中断,CPU并不会响应,比如执行完向ss寄存器传送数据的指令后。因为ss和sp指向栈顶,对他们的设置应该连续完成
第十三章 int指令
知识点
- int指令的格式为:int n,n为中断类型码。引发n号中断的中断进程
- 中断进程:中断处理程序的简称
- BIOS是在ROM存放的一个程序。BIOS和dos都提供了中断进程,BIOS主要包括以下部分
- 硬件的检测和初始化程序
- 外部,内部中断的中断例程
- 用于对硬件设备进行IO操作的中断例程
- 其他硬件系统相关的中断例程
- BIOS和DOS中断例程的安装过程
- 开机后,CPU一加电,初始化CS=0FFFFH,IP=0,FFFF:0有一个跳转指令,跳转到BIOS的硬件系统检测和初始化程序
- 初始化程序将BIOS例程的入口地址都登记在中断向量表中
- 硬件检测和初始化完成后,调用int 19h进行操作系统的引导,从此计算机由操作系统控制
- DOS启动后,除了完成其他工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量
- 一个供程序员调用的中断例程包含多个子程序, 根据ah的参数来决定调用哪个子程序
四个中断例程的子程序
- int 10h是BIOS提供的中断例程
- 当ah=2时,子程序功能为:设置光标位置
mov ah,2 设置光标位置功能
mov bh,0 第0页
mov dh,5 dh放行号
mov dl,12 dl放列号
int 10h - 当ah=9时,子程序功能为:在光标位置显示字符
mov ah,9 在光标显示字符功能
mov al,'a' 字符
mov bl,7 颜色属性
mov bh,0 第0页
mov cx,3 字符重复个数
int 10h
- 当ah=2时,子程序功能为:设置光标位置
- int 21h是DOS提供的中断例程
- 当ah=4ch时,子程序功能为:程序返回功能
mov ax,4c00h
int 21h - 当ah=9时,子程序功能为:在光标位置显示字符串
mov ax,data
mov ds,ax
mov dx,0 只要让ds:dx指向字符串首地址即可,显示的字符串后面要以字符'$'结尾
mov ah,9
int 21h
- 当ah=4ch时,子程序功能为:程序返回功能
第十四章端口
知识点
- CPU把各类存储器和芯片都当作一个总的逻辑存储器看待,称为内存地址空间
- PC系统的接口卡和主板上,装有各种接口芯片。这些外设接口芯片的内部有若干寄存器,CPU将这些寄存器当作端口访问
- CPU将端口进行统一编址,建立起端口地址空间,CPU通过端口地址访问端口
- 端口的读写指令只有in和out
- 在端口的读写指令中,只能使用ax或al来存放数据,访问8位用al,访问16位用ax
shl是逻辑左移指令,功能为:
- 将寄存器或者内存单元的数据左移
- 最后移除的一位写入CF中
- 最低位用0填充
- 如果移动的位数大于1,必须将移动位数放入cl中
- mov al,00000001b
mov cl,3
shi al,cl
- mov al,00000001b
- shr是逻辑右移指令,和shi相反,最高位用0填充
第十五章 外中断
知识点
- CPu通过端口和外部设备进行联系
- 当CPU外部有需要处理的事情发生时,相关芯片向CPu发出相应的中断信息。CPU执行完当前指令后,可以检测到1发送过来的中断信息,引发中断过程
两类外中断源
- 可屏蔽中断
- 当CPU检测到中断信息时,如果flag中的IF=1,CPU 执行完当前指令后执行中断例程,如果IF=0,则不响应可屏蔽中断
- 中断过程中将IF置0的原因是:在进入中断例程后,禁止其他可屏蔽中断
- 不可屏蔽中断
- 不可屏蔽是CPU执行完当前指令,必须响应的中断
- 对于8086CPU, 不可屏蔽中断的类型码固定为2
PC键盘机的处理
- 按下松开键时,键盘中的芯片产生对应的扫描码,扫描码将送入端口,该端口地址为60h。按下时产生通码,送开时产生断码,断码=通码+80h
- 当键盘的输入到达60h端口时,相关芯片向CPU发出中断类型码为9的可屏蔽中断。若IF为1,则响应中断
- BIOS提供的int 9中断例程主要工作如下:
- 读出60h端口的扫描码
- 如果是字符键的扫描码,将扫描码和对应的ASCII送入内存中的BIOS键盘缓冲区;如果是控制键和切换键的扫描码,则将其转化为状态字节,写入内存中存储状态字节的单元
- 对键盘系统进行相关控制,比如向芯片发出应答信息
- BIOS键盘缓冲区是用于存放int 9h中断例程所接收的键盘输入的内存区,最多存储15个键盘输入,一个键盘输入用一个字单元存放,高位扫描码,低位字符码
- 0040:17单元存储键盘状态字节,每一位都有特定含义
第十六章 直接定址表
知识点
- 数据标号:标记了存储数据的单元的地址和长度,在指令中可以代表一个内存单元
- 后面加有“:”的标号,只能在代码段中出现
- 想要直接用数据标号访问数据,需要使用伪指令assume将标号所在的段地址和一个段寄存器联系起来,程序中也需要用指令将段寄存器联系起来
- 可以像定义数据一样定义标号。如果用dw定义标号,则存储的值是标号的偏移地址;如果用dd定义标号,则存储的值是段地址和偏移地址
- seg 标号:获取标号的段地址
- 直接定址表:一种编程技巧?将需要的东西事先存在表里,以后用数据查表即可高效完成任务,空间换时间
第十七章 使用BIOS进行键盘输入和磁盘读写
- 键盘缓冲区使用循环队列实现的
- 键盘输入的过程
- 按下按键,引发9号中断
- 从60h端口读出键的通码
- 检查状态字节,是否有shift或者ctrl按下
- 根据状态字节将正确的ASCII码和扫描码存入缓冲区。低位存储ASCII,高位扫描码
- 字符串的存储空间实际上是一个字符栈
- BIOS提供了中断例程int 13h中的2号子程序来方便程序员访问磁盘
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY