汇编语言学习
文章目录[第一章 基础知识](#-1)[第二章 寄存器(CPU工作原理)](#-1)[第三章 寄存器(内存访问)](#-1)[第四章 第一个汇编程序](#-1)[第五章 【bx】和loop指令](#-1)[第六章 包含多个段的程序](#-1)[第七章 更灵活地定位内存地址](#-1)[第八章 数据处理的两个基本问题](#-1)[第九章 转移指令的原理](#-1)[第十章 call和ret指令](#-1)[第十一章 标志寄存器](#-1)[第十二章 内中断](#-1)[第十三章 int指令](#-1)[第十四章 端口](#-1)[第十五章 外中断](#-1)[第十六章 直接定址表](#-1)[第十七章 使用BIOS进入键盘输入和磁盘读写](#-1)汇编语言 学习笔记(【汇编语言】小甲鱼零基础汇编)第〇章 课程资料1.课件+源代码 2.《汇编语言(第3版) 》王爽著 电子书 3.课后习题答案链接:https://pan.baidu.com/s/1JLdDQwbjdfsMwgDq63lxJw提取码:1YxZB站视频:https://www.bilibili.com/video/BV1Rs411c7HG?spm_id_from=333.788.videopod.sectionsvd_source=e59a22d5a19682d94eb28260a23bf84c环境搭建:https://blog.csdn.net/huangmingcsdn/article/details/137966479第一章 基础知识【学习汇编主要是:学习汇编的编程思想,掌握机器运行的思维】 汇编语言是直接在硬件上工作的编程语言,首先要了解硬件系统的结构,才能有效的应用汇编语言对其编程。 1.汇编课程的研究重点 如何利用硬件系统的编程结构和指令集有效灵活的控制系统进行工作 2.汇编语言的主体是汇编指令 3.汇编指令和机器指令的差别在于指令的表示方法上 汇编指令是机器指令便于记忆的书写格式 4.汇编语言时机器指令的助记符 5.汇编语言的组成 1.汇编指令(机器码的助记符) 2.伪指令(由编译器执行) 3.其他符号(由编译器识别,如:+ - * /) 汇编语言的核心是汇编指令,他决定了汇编语言的特性 6.CPU对存储器的读写 CPU要想进行数据的读写,必须和外部器件(即芯片)进行三类信息的交互 1.地址信息:存储单元的地址 2.控制信息:芯片的选择,读或写命令 3.数据信息:读或写的数据第二章 寄存器(CPU工作原理)CPU=运算器+控制器+【寄存器】,器件之间通过总线相连 8086CPU有14个寄存器,名称分别为: AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW 2.1 通用寄存器 1.8086CPU所有的寄存器都是16位的,可以存放2个字节 2.AX、BX、CX、DX通常用来存放一般性数据 被称为通用寄存器 3.8086上一代CPU中的寄存器都是8位的,为了保证兼容性 这四个寄存器都是可以分为2个独立的8位寄存器使用 AX=AH+AL BX=BH+BL CX=CH+CL DX=DH+DL 4.AX的低8位(0-7)构成AL寄存器 高8位(8-15)构成了AH寄存器 AH和AL寄存器是可以独立使用的8位寄存器 2.2 字在寄存器中的存储 8086一个字16位2.3 几条汇编指令 1.汇编指令不区分大小写 2.几条汇编指令 mov ax,18 ;AX=18 mov ah,78 ;AH=78 add ax,8 ;AX=AX+8 mov ax,bx ;AX=BX add ax,bx ;AX+=BX 3.用目前学过的汇编指令,最多使用四条指令,编程计算2的4次方 mov ax,2 ;ax=2 add ax,ax ;ax=4 add ax,ax ;ax=8 add ax,ax ;ax=16 2.4 物理地址 1.CPU访问内存单元时,要给出内存单元的地址。 2.所有的内存单元够成的存储空间是一个一维的线性空间 3.我们将这个唯一的地址称为物理地址 2.5 16位结构的CPU 16位结构描述了一个淳朴具有以下几个方面特征: 1.运算器一次最多可以处理16位的数据 2.寄存器的最大宽度为16位 3.寄存器和运算器之间的通路是16位的 2.6 8086CPU给出物理地址的方法 1.8086有20位地址总线,可传送20位地址,实际上的寻址能力为1M 2.8086内部为16位结构,它只能传送16位的地址,理论上表现出的寻址能力却只有64K 3.问题:8086CPU如何用内部16位的数据转换成20位的地址? 1.8086CPU采用一种在内部用两个16位地址合成的方法,来形成20位的物理地址 即:段地址+偏移地址=物理地址 2.地址加法器合成物理地址的方法: 物理地址=段地址×16+偏移地址 3.“地址段×16”即是数据左移4位(二进制位的左移4位,十六进制的左移1位) 在地址加法器中,如何完成“段地址×16”? 二进制形式的段地址左移4位 2.7 “段地址×16+偏移地址=物理地址”的本质含义 1.即可以用两个16位的二进制数来表示一个20位的二进制数 2.8086CPU中内部为16位结构,但地址线却是20位的,使用地址加法器可以把16位地址变成20位地址 具体操作就是:段地址×16+偏移地址 2.8 段的概念 1.内存并没有分段,段的划分来自于CPU,由于8086CPU用“段地址×16+偏移地址=物理地址” 的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存 2.以后,在编程时可以根据需要,将若干地址连续的内存单元看作一个段, 使用段地址×16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元 3.注意 1.段地址必然是16的倍数,即一个段的起始地址必然是16的倍数 2.偏移地址为16位,16位地址的寻址能力为64K,所以一个段的长度最大为64K 3.CPU可以用不同的段地址和偏移地址形成同一个物理地址 2.9 段寄存器 1.段寄存器就是提供段地址的 8086CPU有4个段寄存器: 1.CS(code segment) 2.DS(data segment) 3.SS(stack segment) 4.ES(extra segment) 2.当8086CPU要访问内存时,有这4个段寄存器提供内存单元的段地址 2.10 CS和IP 1.CS和IP时候8086CPU中最关键的寄存器 他们指示了CPU当前读取指令的地址。 2.CS和IP的含义 CS:代码段寄存器 IP:指令指针寄存器【专用寄存器】 3.8086CPU工作过程的简要描述 1.从CS:IP指向内存单元,读取指令,读取的指令进入指令缓冲器 2.IP=IP+所读取指令的长度,从而指向下一条指令 3.执行指令,转到步骤1,重复这个过程 4.开机时的CS和IP 1.在8086CPU加电启动或复位后(即CPU刚开始工作时)CS和IP被设置为 CS=FFFFH,IP=0000H 2.即在8086PC机刚启动时,CPU从内存FFFF0H单元中读取指令执行 3.FFFF0H单元中的指令是8086PC机开机后执行的第一条指令 5.修改CS、IP的指令 1.在CPU中,程序员能够【用指令读写】的部件只有【寄存器】, 程序员可以通过改变寄存器中的内容实现对CPU的控制 2.CPU从何处执行指令是由CS、IP中的内容决定的,程序员可以通过改变CS、IP中的内容 控制CPU执行目标指令 3.如何修改CS和IP? 1.通过mov改变AX等,但是不能通过mov改变CS和IP 2.【jmp 段地址:偏移地址】 可以用来同时修改CS和IP 指令中的段地址修改CS 偏移地址修改IP 3.【jmp 某一合法的寄存器】 仅修改IP的内容 比如:jmp ax 或者 jmp bx(类似于mov IP ax) 4.jmp是只具有一个操作对象的指令 2.11 代码段 1.可以将长度为N(N=64KB)的一组代码,存放在一组地址连续、其实地址为16的倍数的内存单元中 这段内存是用来存放代码的,从而定义了一个代码段 2.CPU中只认被CS:IP指向的内存单元中的内容为指令 【实验一】查看CPU和内存,用机器指令和汇编指令编程 1.R命令:查看、改变CPU寄存器的内容 r后面加寄存器的名称可以改变CPU寄存器的内容 2.D命令:查看内存中的内容 3.E命令:改写内存中的内容 4.U命令:将内存汇总的机器指令翻译成汇编指令 5.T命令:执行一条机器指令 6.A命令:以汇编指令的格式在内存中写入一条机器指令 1.debug中输入的默认是16位数 2.空格数量任意 7.按Q可以退出第三章 寄存器(内存访问)3.1 内存中字的存储1.任何两个地址连续的内存单元,N号单元和N+1号单元,可以将他们看成两个存储单元 也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元 2.注意:在内存的表示中,从高到低,是从0号单元开始,然后逐渐变大, 即在书写时,低位写在高的地方,高位写在低的地方, 如上图所示:4E20H即是0号字节存储20,1号字节存储4E 3.2 DS和[address] 1.8086中有一个DS寄存器,通常用来存放要访问的数据的段地址 2.例如:我们要读取10000H单元的内容可以用如下程序段进行: mov bx,1000H mov ds,bx mov al,[0] 上面的三条指令将10000H(1000:0)中的数据读到al中 1.复习:已知mov指令可以完成的两种传送功能 1.将数据直接送入寄存器 2.将一个寄存器中的内容送入另一个寄存器中 2.除此之外,mov指令还可以将一个内存单元中的内容送入一个寄存器 mov指令格式:mov 寄存器名,内存单元地址 [...]表示一个内存单元,“[...]”中的...表示内存单元的【偏移地址】 执行指令时,8086CPU自动取DS中的数据为内存单元的【段地址】 3.如何把1000H放入DS中? 要通过通用寄存器把段地址传入到DS中 8086CPU不支持将数据直接送入段寄存器的操作,DS是一个段寄存器 即:mov ds,1000H 是非法的 数据-通用寄存器-段寄存器 3.写几条指令,将AL中的数据送入内存单元10000H? mov bx,1000H mov ds,bx mov [0],al ;al中的字节型数据送入到1000H:0中 3.3 字的传送 1.8086CPU是16位结构,有16根数据线,所以可以一次性传送16位的数据 即:一次可以传送一个字 2.比如 mov bx,1000H mov ds,bx mov ax,[0] ;1000H:0处的字型数据送入ax中 mov [0],cx ;cx中的16位数据送入到1000H:0中 3.4 mov、add、sub指令 1.复习:已学mov指令的几个形式 1.mov 寄存器,数据 ;立即寻址 2.mov 寄存器,寄存器 ;寄存器寻址 3.mov 寄存器,内存单元 ;直接寻址 4.mov 内存单元,寄存器 ;寄存器寻址? 5.mov 段寄存器,寄存器 ;寄存器寻址 6.mov 寄存器,段寄存器 ;寄存器寻址 2.add、sub同mov一样,都有两个操作对象 1.add的用法 1.add 寄存器,数据 ;立即寻址 2.add 寄存器,寄存器 ;寄存器寻址 3.add 寄存器,内存单元 ;直接寻址 4.add 内存单元,寄存器 ; 2.sub的用法 【不带借位的减法】 指令格式 sub op1,op2 ;意为:op1=op1-op2 1.sub 寄存器,数据 ;立即寻址 2.sub 寄存器,寄存器 ;寄存器寻址 3.sub 寄存器,内存单元 ;直接寻址 4.sub 内存单元,寄存器 ; 3.5 数据段 如何访问数据段中的数据? 将一段内存当作数据段,是我们在编程时的一种安排 具体操作:用DS存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元 3.6 栈 1.8086CPU提供相关的指令来以栈的方式访问内存空间 这意味着,我们在基于8086CPU编程的时候,可以将一段内存当作栈来使用 2.8086CPU提供入栈和出栈指令:(最基本的) push(入栈) pop(出栈) 1.push ax:将寄存器ax中的数据送入栈中 2.pop ax:从栈顶取出数据送入ax 3.8086CPU的入栈和出栈操作都是以【字(16位)】为单位进行的 4.pop和push可以在寄存器和内存之间传送数据 3.CPU如何知道一段内存空间被当做栈使用? 1.8086CPU中,有两个寄存器 1.段寄存器SS:存放栈顶的段地址 2.寄存器SP:存放栈顶的偏移地址【专用寄存器】 2.任意时刻SS:SP指向栈顶元素,当栈为空的时候,也就不存在栈顶元素 ss:sp也就指向栈最高地址单元的下一个单元 4.执行push和pop的时候,如何知道哪个单元是栈顶单元? 1.执行push ax时 1.sp=sp-2 2.将ax中的内容送入到ss:sp指向的内存单元 ss:sp此时指向新栈顶 2.执行pop ax时 1.将ss:sp指向的内存单元的内容送入到ax中 注意:这里取出的内容在内存中还是存在的,并没有被重置 下一轮push会覆盖 2.sp=sp+2 5.如果栈是空的,sp指向哪里? sp指向最高地址单元的下一个单元 3.7 栈顶超界的问题 ss、sp只记录了栈顶的地址,依靠ss、sp可以保证在入栈和出栈时找到栈顶 可以,如何能够保证在入栈、出栈时,栈顶不会超出栈空间? 1.8086CPU不保证栈的操作不会越界 2.当栈空的时候,再执行pop出栈 或者 当栈满的时候再使用push入栈 都会发生栈顶超界问题,会操作到栈以外的数据, 这些数据可能是其他用途的数据或者代码 栈顶超界是危险的!!! 3.8086CPU没有记录栈顶上下限的寄存器 3.8 栈段 1.将一段内存当做栈段,仅仅是我们在编程时的一种安排, 2.ss:sp指向我们定义的栈段的栈顶; 3.当栈空时,sp指向最高地址的下一个单元 4.思考:一个栈段最大可以设为多少? 64KB 5.设栈顶的变化范围是0-FFFFH,从栈空时sp=0(最高地址单元FFFFH的下一个单元0000H) 一直压栈,直到栈满,sp=0; 如果再次压栈,栈顶将环绕,覆盖原来栈中的内容 6.一段内存,既可以是代码的存储空间,又可以是数据的存储空间,还可以是栈空间 也可以是什么都属实。 关键在于CPU中寄存器的设置,即:cs、ip、ss、sp、ds的设置 **可以通过mov直接给sp赋值【立即数寻址】,但是不能通过mov给cs、ip、ss、ds赋值 给cs和ip赋值需要使用jum指令 给ss和ds赋值需要使用mov ss或ds,寄存器 ;【寄存器寻址】 【实验二】第四章 第一个汇编程序4.1 一个源程序从写出到执行的过程 1.一个汇编语言程序从写出到最终执行的简要过程 编写-编译连接-执行 2.对源程序进行编译连接 1.使用汇编语言编译程序(MASM.EXE)对源程序文件中的源程序进行编译,产生目标文件【.obj文件】 2.再用连接程序(LINK.EXE)对目标文件进行连接,生成可在操作系统中直接运行的可执行文件【.EXE文件】。 3.可执行文件包含两部分内容 1.程序(从源程序的汇编指令翻译过来的机器码)和数据(源程序中定义的数据) 2.相关的描述信息(比如:程序有多大、要占多少内存空间等) 4.执行可执行文件中的程序 1.在操作系统(如:MSDOS)中,执行可执行文件中的程序 2.操作系统依照可执行文件中的描述信息,将可执行文件中的机器码和数据加载入内存 并进行相关的初始化(比如:设置CS:IP指向第一条要执行的指令),然后由CPU执行程序 4.2 源程序的主要结构 源程序由 汇编指令+伪指令+宏指令 组成 伪指令:编译器处理 汇编指令:编译为机器码 1.伪指令 1.没有对应的机器码的指令,不能由CPU直接执行 2.伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作 2.segment和ends【定义一个段】 1.segment和ends是一对成对使用的伪指令 2.编写汇编程序【必须】使用到的指令 3.segment和ends的功能是定义一个段 segment:说明一个段开始 ends:说明一个段结束 4.一个段必须有一个名称来标识,使用格式为 段名 segment 段名 ends 5.一个汇编程序由多个段组成 这些段用来存放【代码、数据、或当作栈空间】来使用 一个有意义的汇编程序至少要有一个段,这个段用来存放代码。 3.end【真正的没了】 1.end是一个汇编程序的结束标记 2.编译器在编译汇编程序的过程中,如果碰到了伪指令end,就结束对源程序的编译 3.如果程序写完了,要在结尾处加上伪指令end 否则,编译器无法知道程序在何处结束 4.【切记】不要把end和ends搞混了 end:汇编程序的结束标记 ends:与segment成对出现 4.assume【寄存器和段的关联假设】 1.它假设某一段寄存器和程序中的某一个用segment...ends定义的段相关联 2.通过assume说明这种关联,在需要的情况下, 编译程序可以将段寄存器和某一具体的段相联系 5.程序和源程序 1.我们将源程序文件中的所有内容称为【源程序】 2.将源程序中最终由计算机执行处理的指令或数据称为【程序】 3.程序最先以汇编指令的形式,存储在源程序中 然后经过编译、连接后转变为机器码,存储在可执行文件中 6.标号,标号与段名称有所区别 1.一个标号指代了一个地址,即是段名称。 2.段名称 放在segment的前面,作为一个段的名称 这个段的名称最终将被汇编、连接程序处理为一个段的段地址 7.DOS中的程序运行 1.DOS是一个单任务操作系统 2.一个程序结束后,将CPU的控制权交还给是他得以运行的程序 我们称这个过程为:程序返回 8.程序返回 mov ax,4c00H int 21H ;【中断机制】是DOS最伟大的机制,Windows系统上是【消息机制】 这两条指令所实现的功能就是程序返回 9.几个和结束相关的内容 1.段结束:伪指令 通知编译器一个段的结束【ends】 2.程序结束:伪指令 通知编译器程序的结束【end】 3.程序返回:汇编指令 mov ax,4c00H int 21H 10.语法错误和逻辑错误 1.语法错误 1.程序在编译时被编译器发现的错误 2.容易发现 2.逻辑错误 1.在编写时不会表现出来的错误、在运行时会发生的错误 2.不容易发现 4.3 以简化的方式进行汇编和连接 汇编使用的程序:masm.exe 连接使用的程序:link.exe 简化方式进行汇编和连接的程序:ml.exe 4.4 汇编和连接的作用 连接的作用 1.当源程序很大时,可以将他们分成多个源程序文件夹编译 每个源程序编译成为目标文件后,再用连接程序将它们连接在一起, 生成一个可执行文件 2.程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起 生成一个可执行文件 3.一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接 用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。 所以在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用 连接程序对目标文件进行处理,生成可执行文件 4.5 可执行文件中的程序装入内存并运行的原理 1.在DOS中,可执行文件中的程序P1若要运行,必须有一个正在运行的程序P2 将P1从可执行文件中加载入内存,将CPU的控制权交给P1,P1才能得以运行 2.当P1运行完毕后,应该将CPU的控制权交还给使他得以运行的程序 3.操作系统的外壳 1.操作系统是由多个功能模块组成的庞大、复杂的软件系统 任何通用的操作系统,都需要提供一个称为shell(外壳)的程序, 用户(操作人员)使用这个程序来操作计算机系统工作 2.DOS中有一个程序command.com,这个程序在DOS中称为命令解释器 也就是DOS系统的shell 4.执行可执行文件1.exe时, (1)什么程序将CPU的控制权交给了1.exe? (2)将程序1.exe加载入内存后,如何使程序得以运行? (3)1.exe程序运行结束后,返回到了哪里? 1.在DOS中直接执行1.exe时,是正在运行的cmd.exe将1.exe中的程序加载入内存 2.cmd.exe设置CPU的CS:IP指向程序的第一条指令(即,程序的入口) 从而使程序得以运行 3.程序运行结束后,返回cmd.exe中,CPU继续运行cmd.exe 【实验三】第五章 【bx】和loop指令5.1 [bx] 1.和[0]类似,[0]表示内存单元,它的偏移地址是0; 2.[bx]同样也表示一个内存单元,它的段地址在DS中 它的偏移地址在bx中,至于是取字还是取字节, 要看他放入的寄存器是8位还是16位 3.补充:inc指令:相当于C语言中的++运算符 5.2 Loop指令 这个指令和循环有关 1.指令格式:loop 标号 CPU执行loop指令的时候,要进行两步操作 1.(cx)=(cx)-1; 2.判断cx中的值,若不为零,则转至标号处执行程序 若为零,则向下执行。 2.通常,loop指令实现循环,cx中存放循环的次数 3.标号 在汇编语言中,标号代表了一个地址,标号标识了一个地址 4.使用cx和loop指令相配合实现循环功能的三个要点 1.在cx中存放循环次数 2.loop指令中的标号所标识地址要在前面 3.要循环执行的程序段,要写在标号和loop指令的中间 5.用cx和loop指令相配合实现循环功能的程序框架 mov cx,循环次数 S:循环执行的程序段 loop s 5.3 在Debug中跟踪供loop指令实现的循环程序 **注意:在汇编程序中,数据不能以字母开头,如果要输入像FFFFH这样的数 则要在前面添加一个0 在debug程序中引入G命令和P命令 1.G命令 G命令如果后面不带参数,则一直执行程序,直到程序结束 G命令后面如果带参数,则执行到ip为那个参数地址停止 2.P命令 T命令相当于单步进入(step into) P命令相当于单步通过(step over) 5.4 Debug和汇编编译器Masm对指令的不同处理 1.在debug中,可以直接