Skip to content

Instantly share code, notes, and snippets.

@voldikss
Forked from v4lour/AssemblyLanguage.md
Created October 20, 2017 08:48
Show Gist options
  • Save voldikss/2607045db8b6a1c024887ae30cf3684c to your computer and use it in GitHub Desktop.
Save voldikss/2607045db8b6a1c024887ae30cf3684c to your computer and use it in GitHub Desktop.
汇编语言(王爽著)笔记

汇编语言(王爽著)笔记

@v4lour

第一章 基础知识

汇编语言是直接在硬件上工作的编程语言,由以下3类指令组成:

  • 汇编指令:机器码的助记符,有对应的机器码
  • 伪指令:没有对应的机器码,由编译器执行
  • 其他符号:如+、-等,由编译器识别,没有对应的机器码

汇编语言的核心是汇编指令

指令和数据是应用上的概念。在内存或磁盘中,指令和数据没有任何区别,都是二进制信息。

微机存储器的容量是以字节为最小单位来计算的。一个存储单元可以存储8个bit,即8位二进制。

CPU在读写数据时要指明对哪一个器件进行操作、进行哪种操作、是读还是写操作。

第二章 寄存器

CPU中的主要部件是寄存器,程序员通过改变各种寄存器中的内容来实现对CPU的控制。

8086CPU有14个寄存器——AX, BX, CX, DX, SI, DI, SP, BP, IP, CS, SS, DS, ES, PSW。

一个字节byte有8位,可以存放在8位寄存器中;一个字word有16位,可以存放在16位寄存器中。

CPU访问内存单元时,要给出内存单元地址。所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,这个唯一的地址称为物理地址。

物理地址 = 段地址 * 16 + 偏移地址

CS和IP是8086CPU中最重要的两个寄存器。任何时刻,CPU将CS:IP指向的内容当作指令执行

第三章 寄存器(内存访问)

CPU中用16位寄存器来存储一个字,高8位存放高字节,低8位存放低字节。而在内存中存储时,由于内存单元是字节单元,则一个字需要两个地址连续存放,低位字节存放在低地址单元中,高字节存放在高地址单元中。

push ax表示将寄存器ax中的数据送入栈中,pop ax表示从栈顶取出数据送入ax。

8086CPU的入栈和出栈操作都是以字为单位进行的。

任意时刻SS:SP指向栈顶元素

push、pop操作本质上只是修改SP值。

8086CPU中不能直接向段寄存器中送入数据,需要寄存器ax作为中转。

第四章 第一个程序

汇编程序从写出到执行的过程:编程(Edit)——1.asm——编译(Masm)——1.obj——连接(Link)——1.exe——加载(Command)——内存中的程序——运行(CPU)

程序加载后ds中存放着程序所在内存区的段地址,其偏移地址为0,则程序所在的内存区地址为ds:0;这个内存区的前256个字节中存放的是PSP(程序段前缀),DOS用其和程序进行通信。从256个字节向后的空间存放的是程序。即程序的物理地址是SA*16+0+256=(SA+16)*16+0,用段地址和偏移地址表示为SA+10H:0

第五章 [BX]和loop指令

loop指令实现循环功能,cx中存放循环次数。

在汇编源程序中数据不能以字母开头。

DOS方式下,一般情况,0:200~0:2ff空间中没有系统或其他程序的数据或代码。

第六章 包含多个段的程序

编程的时候需要考虑用多个段来存放数据、代码和栈。用和定义代码段一样的方法来定义多个段,然后在这些段里定义需要的数据,或通过定义数据来取得栈空间。

第七章 更灵活的定位内存地址的方法

and指令,将操作对象的相应位设为0;or指令,将操作对象的相应位设为1。

字母大小写转化问题:小写字母比大写字母大20H;大写字母ASCII码的第5位为0,小写为1。

如果一个问题的解决方案使我们陷入一种矛盾之中,那么,很可能是我们的出发点有了问题,或者,我们起初运用的规律并不合适

我们编写的程序大都是进行数据的处理,而数据在内存中存放,所以我们在处理数据之前首先要搞清楚数据存储在什么地方,即数据的内存地址。

在程序中,用16位寄存器进行内存单元之间的数据传送,一次复制2字节。

不同的寻址方式(即定位内存单元的方法):

  • [idata]用一个常量来表示地址,可用于直接定位一个内存单元
  • [bx]用一个变量来表示内存地址,可用于间接定位一个内存单元
  • [bx+idata]用一个变量和常量来表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元
  • [bx+si]用两个变量表示地址
  • [bx+si+idata]用两个变量和一个常量表示地址

一般来说,在需要暂存数据的时候,我们都应该使用栈

第八章 数据处理的两个基本问题

bx, si, di, bp只有这4个寄存器可以用[...]进行内存单元寻址,可以单独出现,或以4种组合出现:bx和si,bx和di,bp和si,bp和di。

在[...]中使用bp,默认段地址为ss。

汇编语言用3个概念表达数据的位置:立即数(idata)、寄存器和段地址:偏移地址。

8086CPU可以处理两种尺寸的数据:byte和word,所以指令操作必须指明指令进行的是字操作还是字节操作:

  • 通过寄存器指明,如ax进行字操作,al进行字节操作
  • 没有寄存器名存在的情况下,用X ptr指明内存单元长度,X可以为word或byte
  • push指令只进行字操作

第九章 转移指令的原理

可以修改IP,或者同时修改CS和IP的指令统称为转移指令。

操作符offset在汇编语言中是由编译器处理的符号,功能是取得标号的偏移地址。

jmp short 标号,jmp near ptr 标号,jcxz 标号,loop 标号等指令,他们对于IP的修改是根据转移目的地址和转移起始地址之间的位移来进行的。在它们对应的机器码中不包含转移目的地址,而包含的是到目的地址的位移。这种设计,方便了程序段在内存中的浮动装配。

第十章 CALL和RET指令

ret指令用栈中的数据修改IP的内容,实现近转移;相当于进行pop IP。 retf指令用栈中的数据修改CS和IP的内容,实现远转移;相当于进行pop IP; pop CS。

CPU执行call指令时进行两步操作:将**当前的IP或CS和IP(即call指令后一条指令)**压入栈中;转移。

模块化程序设计中,用寄存器来存储参数和结果是最常用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序读写恰恰相反:调用者将参数送入参数寄存器,从结果寄存器中取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。

编写子程序的标准框架:

  • 子程序中使用的寄存器入栈
  • 子程序内容
  • 子程序中使用的寄存器出栈
  • 返回(ret, retf)

第十一章 标志寄存器

标志寄存器具有以下三种作用:

  • 用来存储相关指令的某些执行结果
  • 用来为CPU执行相关指令提供行为依据
  • 用来控制CPU的相关工作方式

8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW)。

标志寄存器按位起作用,即它的每一位具有专门的含义:

  • ZF:零标志位。结果为0,zf=1,否则为0。
  • PF:奇偶标志位。如果1的个数为偶数,pf=1;否则为0。
  • SF:符号标志位。如果结果为负,sf=1。
  • CF:进位标志位。用于无符号数运算。
  • OF:溢出标志位。用于有符号数运算。
  • DF:方向标志位。df=0表示每次操作后si,di递增,反之递减。cld指令将df置0,std指令将df置1。

pushf将标志寄存器的值入栈,popf将其值出栈。

第十二章 内中断

对于8086CPU,当CPU内部有下面情况发生时,将产生中断信息:

  • 除法错误(0)
  • 单步执行(1)
  • 执行into指令(4)
  • 执行int指令(n)

中断信息通过中断类型码识别,中断类型码为一个字节型数据,可表示256种中断信息来源。

用来处理中断信息的程序被称为中断处理程序,即中断例程。

中断向量表就是中断向量的列表,中断向量就是中断处理程序的入口地址。所以,中断向量表就是中断处理程序入口地址的列表

中断向量表存放在内存中,8086CPU中中断向量表放在地址0处,内存0000:0000~0000:03FF的1024个内存单元中存放着中断向量表。

对于8086CPU,一个表项占两个字,高地址字存放段地址,低地址字存放偏移地址

用中断类型码找到中断向量,并用它设置CS和IP,这个工作是由CPU硬件自动完成的。这个工作的过程被称为中断过程。

中断过程:

  • 取得中断类型码N
  • pushf
  • TF=0,IF=0
  • push CS
  • push IP
  • (IP)=(N4), (CS)=(N4+2)

iret指令为:pop IP;pop CS;popf

第十三章 int指令

int指令格式为:int n,功能是引发n号中断过程。

编写供应用程序调用的中断例程步骤:

  • 编写子程序
  • 安装程序到内存某处(如0:200)
  • 设置中断向量表,将程序的入口保存在相关表项中,使其称为中断例程

在系统板的ROM中存放着一套程序,称为BIOS(基本输入输出系统),BIOS中主要包含以下几部分内容:

  • 硬件系统的检测和初始化程序
  • 外部中断和内部中断的中断例程
  • 用于硬件设备进行I/O操作的中断例程
  • 其他和硬件系统相关的中断例程

BIOS和DOS提供的中断例程如何安装到内存中?

  • 开机后,CPU一加电,初始化(CS)=0FFFFH,(IP)=0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的操作系统检测和初始化程序
  • 初始化程序将建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记到中断向量表中。注意,对于BIOS所提供的中断例程,只需将入口地址登记在中断向量表中,因为他们是固化到ROM中的程序,一直在内存中存在
  • 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导,从此将计算机转交给操作系统
  • DOS启动后,除完成其他工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量

第十四章 端口

CPU可以直接读写3个地方的数据:

  • CPU内部的寄存器
  • 内存单元
  • 端口

在PC系统中,CPU最多可以定位64KB个不同的端口,端口范围为0~65535。

端口的指令读写只有两条:in和out,分别用于从端口读取数据和往端口写入数据。

在in和out指令中,只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。访问8位端口时用al,访问16位端口时用ax。

对0~255以内的端口进行读写时:

in al, 20h;从20h端口读入一个字节
out 20h, al;往20h端口写入一个字节

对256~65535的端口进行读写时,端口号放在dx中:

mov dx, 3f8h;将端口号3f8h送入dx
in al, dx;从3f8h端口读入一个字节
out dx, al;往3f8h端口写入一个字节

第十五章 外中断

外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中;CPU向外设的输出也不是直接送入外设,而是先送入端口中,再由相关芯片送到外设。

PC系统中外中断源共有两类:可屏蔽中断和不可屏蔽中断。

可屏蔽中断指CPU可以不响应的外中断,CPU是否响应可屏蔽中断要看标志寄存器的IF位的值。如果IF=1,则CPU执行完当前指令后响应中断,引发中断过程。若为0则不响应。

8086CPU提供设置IF的指令为:sti设置IF=1;cli设置IF=0。

不可屏蔽中断是CPU必须响应的外中断。对于8086CPU,不可屏蔽中断的中断类型码固定为2。

键盘上每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描,一般将按下一个键时产生的扫描称为通码,松开一个键产生的扫描码称为断码。扫描码长度为一个字节,通码的第7位为0,断码的第7位为1。

断码 = 通码 + 80h

指令系统总结:

  • 数据传送指令:mov,push,pop,pushf,popf,xchg等
  • 算术运算指令:add,sub,adc,sbb,inc,dec,cmp,imul,idiv,aaa等。执行结果影响标志寄存器的sf、zf、of、cf、pf、af。
  • 逻辑指令:and,or,not,nor,xor,test,shl,shr,sal,sar,rol,ror,rcl,rcr等。执行结果都影响标志寄存器的相关标志位。
  • 转移指令
    • 无条件转移指令:jmp
    • 条件转移指令:jcxz,je,jb,ja,jnb,jna
    • 循环指令:loop
    • 过程:call,ret,retf
    • 中断:int,iret
  • 处理机控制指令:cld,std,cli,sti,nop,clc,cmc,stc,hlt,wait,esc,lock
  • 串处理指令:movsb,movsw,cmps,scas,lods,stos。一般与rep,repe,repne等前缀指令结合使用。

第十六章 直接定址表

a: db 1,2,3,4,5,6,7,8
b: dw 0

;a,b仅仅表示了内存单元的地址

a db 1,2,3,4,5,6,7,8
b dw 0

;a,b不仅描述内存地址,也描述单元长度。标号a,描述了地址code:0,和从这个地址开始,以后的内存单元都是字节单元;标号b描述了地址code:8,和从这个地址开始,以后的内存单元都是字单元。

;这种标号称为数据标号,标记了存储数据的地址和长度。

在后面加有“:”的地址标号,只能在代码段中使用,不能在其他段中使用。

如果想在代码段中直接使用数据标号访问数据,则需要用伪指令assume将标号所在的段和一个段寄存器联系起来。

直接定址表:通过依据数据,直接计算出所要找的元素的位置的表。

第十七章 使用BIOS进行键盘输入和磁盘读写

键盘输入引发9号中断,BIOS提供int9中断例程。一般的键盘输入,在CPU执行完int9中断例程后,都放到了键盘缓冲区。

键盘缓冲区的字单元中,高字节存储扫描码,低字节存储ASCII码。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment