汇编语言(以8086为例)

汇编语言(以8086为例)

【通俗易懂的汇编语言(王爽老师的书)】

01-序言

P3 由机器指令到汇编指令

机器语言: 是机器指令的集合。

机器指令: 是一台机器可以正确执行的命令。机器指令由一串二进制数表示,例如01010000。

由于机器指令的可读性极差,所以有了汇编语言与汇编指令。

‍

汇编语言: 的主体是汇编指令。

汇编指令和机器指令表达的意思是相同的,他们的差别在于指令的表示方法上:汇编指令是机器指令便于记忆的书写形式;汇编指令是机器指令的助记符。

机器指令:1000100111011000

汇编指令:MOV AX BX

操作:将寄存器BX的内容送到AX中

‍

工作过程: 汇编语言-> 编译器-> 机器码-> 计算机执行

‍

汇编语言程序包含:

汇编指令——机器码的助记符

伪指令——由编译器执行

其他符号——由编译器识别

‍

P4 计算机的组成

​​

CPU想要工作,就必须向它提供指令和数据。而指令和数据在存储器(内存)中存放。

‍

指令和数据的表示: 数据和指令存储在内存或磁盘上;数据和指令都是二进制信息。

‍

存储单元: 存储器被划分为若干个存储单元,每个存储单元从0开始顺序编号;实际的内存空间很“大”。例如8086有20条数据线,寻址空间为1MB。

​

总线: 从物理上将,总线是一根根导线的集合;逻辑上则将总线分为地址总线、数据总线、控制总线三种总线。

CPU通过地址总线来指定存储单元,地址总线的宽度决定了可寻址的存储单元的大小,N根地址总线对应2N的寻址空间。

CPU与内存或其他器件之间的数据传输是通过数据总线来进行的,数据总线的宽度决定了CPU和外界的数据传送的速度。

CPU通过控制总线对外部器件进行控制,控制总线是一些不同控制线的集合,控制总线的宽度决定了CPU对外部器件的控制能力。

​​​​​​

‍

P5 内存的读写和地址空间

CPU想要进行数据的读写,必须和外部器件进行三类信息的交互:

存储单元的地址(地址信息)

器件的选择,读/写命令(控制信息)

读或写的数据(数据信息)

‍

内存地址空间: 可用于存储数据和指令的内存地址的范围。由地址总线决定。

从CPU看地址空间:

将各类存储器看作一个逻辑存储器——统一编址

每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间(下图左侧为8086)

​​

独立编址

‍

P6 语言实践环境搭建

本课以8086CPU为例进行讲解。

因此实践方案为——DOS环境。具体软件为DOSBox

‍

02-访问寄存器和内存

P7 本章导言

​​​​

‍

P8 寄存器及数据存储

寄存器: 是CPU内部的信息存储单元

8086CPU有14个寄存器: 8086CPU所有的寄存器都是16位的,可以存放两个字节,存放的最大值为 216-1

通用寄存器:AX、BX、CX、DX

变址寄存器:SI、DI

指针寄存器:SP、BP

指令指针寄存器:IP

段寄存器:CS、SS、DS、ES

标志寄存器:PSW

​​​​

‍

但是有个问题,8086上一代CPU中的寄存器都是8位的,如何保证程序的兼容性?

解决方案: 将通用寄存器均分为两个独立的8位寄存器使用。例如,AX可以分为AH(high)和AL(low)。

‍

8086是16位CPU,所以8086的字长(word size) 为16bit

一个字(word) 可以存放在一个16位寄存器中,这个字的高位字节存放在这个寄存器的高8位寄存器,这个字的低位字节存放在这个寄存器的低8位寄存器。

​​

‍

P9 mov和add指令

在用中学

​​​​

懂了吧!那么直接开始做题

例01:注意溢出哦,溢出直接舍掉

​​

例02:注意溢出哦,溢出直接舍掉,不能进到AH中

​​

‍

P10 确定物理地址的方法

CPU访问内存单元时要给出内存单元的地址。

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

‍

8086有20位地址总线,可传送20位地址,寻址能力为1M。

8086是16位结构的CPU,运算器一次最多可以处理16位的数据,寄存器的最大宽度为16位。

那么在8086内部处理、传输、暂存的地址也是16位,寻址能力也只有16位。可是它有20位地址总线,剩下的难道浪费了吗?

8086给出了一个解决方案:用两个16位地址(段地址、偏移地址)合成一个20位的物理地址。

地址加法器合成物理地址的方法:物理地址 = 段地址*16+偏移地址

对于一个物理地址,可以通过不同的段地址和偏移地址获得。

​​​​

‍

P11 内存的分段表示法

已知8086CPU用“物理地址 = 段地址*16+偏移地址”的方式给出内存单元的物理地址。

段地址,就是分段的方式管理内存。但是要注意,内存本身并没有分段,段的划分来自于CPU。

‍

根据合成物理地址的方法,可以得出两个结论:

因为物理地址 = 段地址*16+偏移地址,段地址被*16了,所以一个段的起始地址一定是16的倍数(即最后一位一定为0,12340H这样)

偏移地址为16位,16位地址的寻址能力为64K,所以一个段的最大长度为64K

‍

8086中存储单元地址的表示方法:

例如,数据在21F60H内存单元中,段地址是2000H,那么有两种表示方法:

数据存在内存 2000:1F60 单元中

数据存在内存的 2000H 段中的 1F60H 单元中

‍

物理地址 = 段地址*16+偏移地址,那段地址和偏移地址分别怎么来呢?

段地址很重要!所以8086提供了4个段寄存器,专门用于存放段地址:

CS——代码段寄存器

DS——数据段寄存器

SS——栈段寄存器

ES——附加段寄存器

而偏移地址可以用多种方法提供——8086有丰富的取址方式

‍

P12 Debug的使用

Debug是DOS系统中著名的调试程序,也可以运行在windows系统上

使用Debug程序,可以查看CPU各种寄存器中的内容、内存的情况,并且在机器指令级跟踪程序的运行

‍

具体操作:

打开DOSBox,先进行挂载,选中工作目录

mount c d:\0105\MASM //将Dos里的c盘,绑定到本机上d:\0105\MASM这个目录

c: //切换到c盘

dir //ls,确定一下挂载是否成功

debug的使用

debug //启动程序,看到'-',说明启动成功

查看寄存器:R命令

r //查看寄存器内容

r [寄存器名] //改变指定寄存器内容,输入在冒号时输入改变值

r[寄存器名] //不加空格也可以

​​​​

‍

查看内存中的内容:D命令

d //列出预设地址内存处,128个字节的内容

d [段地址:偏移地址] //列出内存中指定地址处,128个字节的内容dd

d [段地址:偏移地址] [结尾偏移地址] //列出内存中指定地址处,指定范围的内容

​​

​​

‍

改变内存中的内容:E命令

e [段地址:偏移地址] [数据1] [数据2] //默认是16进制,不需要加'H'

e [段地址:偏移地址] //逐个询问式修改。空格=接受,继续。回车=结束

​​

​​

‍

将机器指令翻译成汇编指令:U命令

u [段地址:偏移地址]

​​

‍

以汇编指令的格式在内存中写入机器指令:A命令

a [段地址:偏移地址] //逐个询问式。空白内容时回车即可退出。

​​

​​

‍

执行机器指令:T命令

t //执行 CS:IP 处的指令

​​

‍

退出debug:Q命令

q //quit

​​

‍

P13 CS、IP与代码段

CS: 代码段寄存器

IP: 指令指针寄存器

CS:IP: CPU将内存中 CS:IP 指向的内容当做指令执行

‍

8086工作过程简要描述:

从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;

IP = IP+所读取指令的长度,从而指向下一条指令;

执行指令。转到步骤(1),重复这个过程

‍

P14 jmp指令

当我们需要修改下一条将要执行的指令时,本质上是需要修改CS、IP的值

那么该如何改变CS、IP的值呢?

使用debug直接修改——不靠谱;

用mov指令修改CS、IP寄存器——有些操作时不合法的​

​

所以答案是,jmp指令: 修改CS、IP的指令

使用方法:

同时修改CS、IP的内容

​jmp [段地址:偏移地址]​。例如:jmp 2000:3​

功能:用指令中给出的段地址修改CS,偏移地址修改IP

仅修改IP的内容

​jmp [某一合法寄存器]​。例如:jmp ax​

功能:类似于mov IP,ax​

‍

jmp指令实践:

​​​​​​​​

‍

P15 内存中字的存储

对于8086CPU,16位作为一个字。

16位的字存储在一个16位的寄存器中,高8位放高字节,低8位放低字节。

16位的字在内存中需要2个连续字节存储,低位字节存放在低地址单元,高位字节存放在高地址单元。

例如,存放4E20H 和 0012H

​​​​

‍

字单元: 由两个地址连续的内存单元组成,存放一个字型数据(16位)

原理: 8086一个字是16位,需要内存中两个连续的字节来存储。

‍

P16 用DS和[address]实现字的传送

CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址。而内存地址由段地址和偏移地址组成。怎样同时给出两个地址呢?

解决方案:DS和[address]配合

用DS寄存器存放要访问的数据的段地址

偏移地址用[...]的形式直接给出

注意:不能直接给DS赋值,只能通过通用寄存器给DS赋值

注意区分是,读写一个字还是一个字型

​​

​​

‍

P17 DS与数据段

要求:实现对内存单元中数据的访问

处理方法:(DS):([address])​。用DS存放数据段的段地址;用相关指令访问数据段中的具体单元,单元地址由[address]指出

将123B0H~123BAH的内存单元定义为数据段:

​​

练习:

​​

‍

总结(不一定完全)

用mov指令操作数据:

指令形式

示例

mov 寄存器, 数据

mov ax,8

mov 寄存器, 寄存器

mov ax,bx

mov 寄存器, 内存单元

mov ax,[0]

mov 内存单元, 寄存器

mov [0],ax

mov 段寄存器, 寄存器

mov ds,ax

mov 寄存器, 段寄存器

mov ax,ds

mov 内存单元, 段寄存器

mov [0],ds

mov 段寄存器, 内存单元

mov ds,[0]

add和sub指令:

指令形式

示例

add 寄存器, 数据

add ax,8

add 寄存器, 寄存器

add ax,bx

add 寄存器, 内存单元

add ax,[0]

add 内存单元, 寄存器

add [0],ax

‍

用DS和[address]形式访问内存中数据段方法小结:

字在内存中存储时,要用两个地址连续的内存单元来存放。字的低位字节存放在低地址单元中,高位字节存放在高地址单元中

用mov指令访问内存单元时,可以在mov指令中只给出单元的偏移地址,此时段地址默认在DS寄存器中

[address]表示一个偏移地址位address的内存单元

在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器对应,低地址单元和低8位寄存器对应

mov 、add 、sub是具有两个操作对象的指令,访问内存中的数据段。

jmp是具有一个操作对象的指令,对应内存中的代码段

‍

P18 栈及栈操作的实现

栈:先进后出。每次操作最顶上的元素。

栈有两个基本的操作:入栈和出栈

‍

现在的CPU都有栈的设计,例如8086就提供相关的指令,支持用栈的方式访问内存空间。

主要有两个指令:

push:入栈。push ax​,将ax的数据送入栈中

pop:出栈。pop ax​,从栈顶取出数据送入ax

push ax:

SP = SP-2

将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶

pop ax:

将SS:SP指向的内存单元处的数据送入ax中

SP = SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

push、pop实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据。与mov指令不同的是,Push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出。

‍

提问:

CPU如何知道一段内存空间被当作栈使用?

执行push和pop的时候,如何知道哪个单元是栈顶单元?

回答:

在8086CPU中,由两个与栈相关的寄存器——SS和SP

栈段寄存器SS——存放栈顶的段地址,栈顶指针寄存器SP——存放栈顶的偏移地址

任何时刻,​**SS:SP**​指向栈顶元素。

注意,栈空间是从高地址向低地址增加的。

​​

‍

如何避免栈顶超界问题?

8086CPU并不能保证栈不会超界。

‍

P19 关于“段”的总结

‍

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

编程时,可以根据需要,将一组内存单元定义为一个段

起始地址为16的倍数,长度小于等于64K的一组地址连续的内存单元,定义为一个段

将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内单元——在程序中可以完全由程序员安排。

‍

目前为止已经学习了三种段:

数据段

将段地址放在DS中

用mov、add、sub等访问内存单元的指令时,CPU将我们定义的数据段中的内容当作数据段来访问

代码段

将段地址放在CS中,将段中第一条指令的偏移地址放在IP中。CS:IP指向即将执行的指令

CPU将执行我们定义的代码段中的指令

栈段

将段地址放在SS中,将栈顶单元的偏移地址放在SP中。SS:SP指向栈顶

CPU在需要进行栈操作(push、pop)时,将我们定义的栈段当作栈空间来使用。

‍

03-汇编语言程序

P20 本章导学

​​

‍

P21 用汇编语言写的源程序

汇编程序结构

汇编程序: 包含汇编指令和伪指令的文本。

汇编程序的注释是;​,默认为10进制

汇编程序->编译器->机器码

汇编程序可以是:

在Debug中直接写入指令编写的汇编程序

适用于功能简单、短小精悍的程序

只需要包含汇编指令即可

单独编写成源文件后再编译为可执行文件的程序

适用于编写大程序

不只有汇编指令,还有指导编译器工作的伪指令

源程序由一些段构成,这些段存放代码、数据,或将某个段当作栈空间

​

‍

伪指令

​​​

程序中的三种伪指令:

段定义

一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或者当作栈空间来使用

一个有意义的汇编程序中至少要有一个段,这个段用来存放代码

定义程序中的段:每个段都需要有段名

格式:段名 segment​——段的开始;段名 ends​——段的结束

end(不是ends)

汇编程序的结束标记。若程序结尾处不加end,编译器在编译程序时,不知道程序在何处结束。

assume(假设)

含义是,假设某一段段寄存器和程序中的某个用segment...ends​定义的段相关联。

格式:assume 寄存器:段名​

例如,assume cs:codesg​指cs寄存器与codesg这个段关联,将定义的codesg当作程序的代码段来使用

‍

如何写出一个程序来

定义一个段

实现处理任务

指出程序在何处结束

段与段寄存器关联

加上程序返回的代码

以实现2*3的程序为例:

​​

‍

程序中可能的错误

语法错误

程序在编译时被编译器发现的错误,容易被发现

逻辑错误

在运行时发生的错误,不容易被发现

‍

P22 由源程序到程序运行

由源程序到程序运行的过程

编写 -> 源程序文件.asm​ -> 编译 -> 目标文件.obj​ -> 连接 ->可执行文件.exe​

‍

编译

软件/指令:masm​

​

​

​​​​

‍

连接

软件/指令:link​

​​

​​

​​

‍

P23 用debug跟踪程序的执行

用debug装载程序:debug file.exe​​

有256(100H)字节的程序段前缀(PSP),作为数据区

​​​​

用p命令/g命令执行:

​​

‍

​​

‍

p24 [...]​和(...)​​

​​

​[...]​:表示一个内存单元。是汇编语法规定的表示形式

​(...)​:表示一个内存单元(只能用物理地址)或寄存器中的内容。是为学习方便大家约定的内容。

​​

‍

​​

‍

p25 Loop指令

功能:实现循环(计数型循环)

指令格式:​loop 标号​

CPU执行loop指令时的操作:

(cx) = (cx) -1

判断cx中的值,不为零则转至标号处执行程序;如果为零则向下执行。

注意:cx中要提前存放循环次数,循环多少次则cx设置为多少;并且需要定义一个标号。

​​​​

​​

用loop指令编程实例:

​​

​​

‍

p26 Loop指令使用再例

​

注意,一定要分析,累加之后,是否有可能超过dx的存储能力

‍

如果你在编写的时候遇到了这个问题:mov ax,[6]​被编译成了mov ax,6​

别急,看下一章

‍

p27 段前缀的使用

​​

为了解决这个“异常”现象,对策是:在[idata]前面显式地写上段寄存器。

​​

这些出现在访问内存单元的指令中,用于显式的指明内存单元的段地址的'ds、cs、ss、es',在汇编语言中被称为段前缀​。

‍

​​

‍

p28 在代码段中使用数据

​

对策:

在程序的段中存放数据,运行时由操作系统分配空间。

段的类别:数据段、代码段、栈段

各类段中均可由数据

可以在单个的段中安置,也可以将数据、代码、栈放入不同的段中

‍

​​

dw(define word):定义一个字

db:定义一个字节

dd:定义一个双字

‍

​​

直接在代码段中添加数据的程序有问题,因为CPU并不知道代码段是从cs:0010​开始的,还将IP设置为0000。

所以怎么让代码从cs:0010​开始呢?

用​标号​标记程序的开始

​​

‍

p29 在代码段中使用栈

​​

​​

‍

p30 将数据、代码、栈放入不同段

将数据、栈、代码放在一个段的缺点:

程序显得混乱,编程和阅读时都要注意何处是数据、何处是栈、何处是代码;

只能应用于要处理的数据很少的,栈空间也小,代码也少的情况。

解决方法:将数据、代码、栈放入不同段

注意代码段仍然需要标号标记

注意:DS是程序开始的位置,但是后面紧跟着100H的PSP,所以DS:100H​才是数据开始的地方。

​​

‍

04-内存寻址方式

P31 本章导学

​​

P32 处理字符问题

汇编程序中,用'abc'​的方式指明数据是以字符形式给出的。编译器将它们转化成相对应的ASCII码。ascii码是一字节大小的数据。

​

可根据这个,做大小写转换:

小写转大写:and 1101 1111

大写转小写:or 0010 0000

​​

‍

P33 [bx+idata]方式寻址

​[bx+idata]​表示一个内存单元,它的偏移地址为(bx)+idata,即bx中的数据加上idata

​mov ax,[bx+idata]/mov ax,[idata+bx]​的含义:(ax) = ((ds)*16+200+(bx))​。

​mov ax,[bx+idata]​的其他常见写法:mov ax,200[bx]/mov ax,[bx].200​

应用: 做数组处理。可将(ds)*16+idata​看作基址,将bx看做偏移量。

‍

P34 SI和DI寄存器

SI和DI是8086CPU中和BX功能相近的寄存器。

SI: source index,源变址寄存器。

DI: destination index,目标变址寄存器。

区别: SI和DI不能分成两个8位寄存器来用。

应用问题: 用寄存器SI和DI实现将字符串'welcome to masm!'复制到它后面的数据区中。

解决方法: 用DS:SI​指向要复制的原始字符串,用DS:DI​指向目的空间。用一个循环来完成复制

‍

P35 [bx+si]和[bx+di]方式寻址

[bx+si]:表示一个内存单元,偏移地址为(BX)+(SI)​。将BX称作基址,SI称作变址。 基址变址寻址。

‍

P36 [bx+si+idata]和[bx+di+idata]方式寻址

[bx+si+idata]:表示一个内存单元,偏移地址为(BX)+(SI)+Idata​。

其他不同的写法:mov ax,[bx+200+si] / mov ax,[200+bx+si] / mov ax,200[bx][si] / mov ax,[bx].200[si] / mov ax,[bx][si].200​

‍

P37 不同寻址方式的灵活应用

对内存的寻址方式

形式

名称

特点

意义

示例

[idata]

直接寻址

用一个常量/立即数来表示地址

用于直接定位一个内存单元

mov ax,[200]

[bx]

寄存器间接寻址

用一个变量来表示内存地址

用于间接定位一个内存单元

mov bx,0mov ax,[bx]

[bx+idata]

寄存器相对寻址

用一个变量和一个常量表示地址

可在一个起始地址的基础上,用变量间接定位内存单元

mov bx,4mov ax[bx+200]

[bx+si]

基址变址寻址

用两个变量表示地址

mov ax,[bx+si]

[bx+si+idata]

相对基址变址寻址

用两个变量和一个常量表示地址

mov ax,[bx+si+200]

‍

案例1:将datasg段中每个单词的头一个字母改为大写字母

​​

​​

案例2:将datasg段中每个单词都改为大写字母

如何解决需要进行双重循环,但只有一个cx的冲突:

法一: 用dx暂存cx

缺陷: CPU中寄存器有限,这样的话dx就会被占用,太浪费

​​

​法二: ​用固定空间保存数据

缺陷: 在运行过程中,该数据有被覆盖的风险

​​

法三: 用栈保存数据

​​

‍

P38 不同寻址方式演示

​​

‍

P39 用于内存寻址的寄存器

只有​**bx、bp、si、di**​可以用在​ **[...]**​内对内存单元进行寻址

bx以外的通用寄存器、段寄存器不可以用在[...]内

**bx、bp、si、di**​的搭配:

​

bx、bp的区别: bx默认ds段;bp默认ss段

‍

P40 在哪里?有多长?

​

​​​

‍

P41 寻址方式的综合应用

要求: 编程修改内存中的过时数据。

​​

‍

P42 用div指令实现触发

​​

切记,提前在默认的寄存器中设置好被除数,且默认寄存器不作别的用处。

‍

P43 用dup设置内存空间

功能:dup和db、dw、dd等数据定义伪指令配合使用,用于进行数据重复。

​​

使用格式:db [重复次数] dup ('重复的内容')​

‍

05-流程转移与子程序

P44 本章导学

​​

P45 ”转移“综述

需求: 在时间过程中,长需要改变程序执行的流程

转移指令:

可以控制CPU执行内存中某处代码的指令

可以修改IP,或同时修改CS和IP的指令

转移指令的分类:

按转移行为

段内转移:只修改IP,如jmp ax​

段间转移:同时修改CS和IP,如jmp 1000:0​

根据指令对IP修改的范围不同

段内短转移:IP修改范围为-128~127​,用一个字节来表示

段内近转移:IP修改范围为-32768~32767​,用一个字来表示

按转移指令

无条件转义指令:jmp

条件转移指令:jcxz

循环指令:loop

过程

终端

P46 操作符offset

格式:​offset 标号​。如offset start​

用法: 通过offset​获取标号所在指令的偏移地址

​​

P47 jmp指令

jmp指令的功能:无条件转移,可以只修改IP,也可以同时修改CS和IP

jmp指令的用法:jmp short 标号​

​​

注意:jmp short​的机器指令中,包含的是跳转目标指令和IP的相对位置,而不是目标地址

‍

两种段内转移:

​jmp short 标号​:

功能:(IP)=(IP)+8位位移

核心:8位位移 = "标号"的地址-jmp指令后的第一个字节的地址;8位位移由编译程序在编译时算出

​jmp near ptr 标号​:

功能:(IP)=(IP)+16位位移

核心:8位位移 = "标号"的地址-jmp指令后的第一个字节的地址;8位位移由编译程序在编译时算出

​​

‍

远转移:​jmp far ptr 标号​直接指明了跳转到的目的地址,即包含了标号的段地址CS和偏移地址IP

​​

‍

转移地址在寄存器中的jmp:​jmp 16位寄存器​

功能:IP = (16位寄存器)

例如:jmp ax​

‍

转移地址在内存中的jmp指令

注意:段地址在高位,偏移地址在低位;段地址和偏移地址各用一个字保存

​

‍

​​

‍

P48 其他转移指令

jcxz指令

格式:​jcxz 标号​

功能: 如果(cx)=0,则跳转到标号处执行;当(cx)≠0时,程序向下执行

​​

‍

loop指令

​​

‍

​​

‍

P49 call 指令和 ret 指令

​call 标号​:调用子程序

​ret​:从子程序返回

实质: 都是流程转移指令,它们都修改IP,或同时修改CS和IP

‍

call指令

call指令进行的两步操作:

将当前的 IP 或 CS和IP 压入栈中;

相当于:push IP​

转移到标号处执行指令

相当于:jmp near ptr 标号​

​​

​​

​​

​

ret指令

​​

‍

P50 call 和 ret 的配合使用

​​

​​

‍

P51 mul指令

​​

‍

P52 汇编语言的模块化程序设计

​​

​​

​​

​​

‍

P53 寄存器冲突的问题

​​

​​

实际上就是计组里讲的,“保护现场“

​​

P54 标志寄存器

​​

​​

​​

​​

​​

​​

​​

‍

P55 带进(错)位的加减法

adc

格式:​adc 操作对象1,操作对象2​

功能: 操作对象1 = 操作对象1+操作对象2+CF

adc是带进位加法指令,利用了CF位上记录的进位值

​​

‍

​​

两个讨论的回答:

不可以。sub ax,ax​是为了将CF置为零

不可以。inc di​是不会产生进位的;但add di,2​是有可能产生进位标记的

sbb

格式:​sbb 操作对象1,操作对象2​

功能: 操作对象1 = 操作对象1-操作对象2-CF

sbb是带借位减法指令,利用了CF位上记录的借位值

​​

‍

P56 cmp和条件转移指令

cmp

格式:​cmp 操作对象1,操作对象2​

功能: 操作对象1-操作对象2

注意: cmp是比较指令,功能相当于减法指令,只是不保存结果,只影响标志寄存器

应用: 用标志寄存器的值来确定比较结果

​​

​​

​​

条件转移指令

​​

​​

‍

P57 条件转移指令应用

​​

‍

P58 DF标志和串传送指令

串传送指令

​​

‍

rep指令

​​

‍

**rep+movsb/movsw**​能高效地成片传送数据

‍

06-中断及外部设备操作

P59 阶段导学

​​

P60 移位指令

​​

逻辑左移(SHL):

将最高位(最左边的)移入CF中,其他位向左移一位,末尾补零

相当于,X=X*2

循环左移(ROL):

将最高位(最左边的)移入CF中,其他位向左移一位,末尾最高位

逻辑右移(SHR):

将最低位(最右边的)移入CF中,其他位向右移一位,首位补零

相当于,X=X/2

算术右移(SAR):

将最低位(最右边的)移入CF中,其他位向右移一位,最高位不变

最高位不变是为了保证“符号”不变。通常情况下,最高位是符号位

​​

当移动位数大于1时,必须使用cl​表示移动位数。

‍

P61 操作显存数据

​​

屏幕上的内容 = 显存中的数据。 所以想要操作屏幕上的内容,就要操作显存中的数据。

更具体的说,就是需要操作B8000h~BFFFFh​,即显示缓冲区中的数据。

​​

每一个显示内容需要两个字节。 低位字节存储要显示符号的ASCII码,高位字节存储显示属性,即颜色。其中高位字节中内容的前四位,是存储的背景颜色;后四位是存储的前景颜色。

​​

‍

P62 描述内存单元的标号

​​

简化版offset。

当没有冒号的时候,被称为数据标号,数据标号能同时描述内存地址和单元长度(即db=字节,dw=字)

​​

‍

​​

数据标号描述的内存地址,是包括段地址的。

扩展用法:将标号当做数据来定义(用c语言来做通俗解释就是:指针的指针)

‍

P63 数据的直接定址表

​​

​ ​

直接定址表

问题求解思路: 利用表,在两个数据集合之间建立一种映射关系,用查表的方法根据给出的数据得出其在另一集合中的对应数据。

优点:

算法清晰和简洁

加快运算速度

使程序易于扩充

空间换时间的方案

‍

​​

​​

‍

P64 代码的直接定址表(NO)

​​

​​

​​

‍

P65 中断及其处理

​​

​​

​​

低位保存IP,高位保存CS

​​

​​

‍

P66 编制中断处理程序

​​

中断向量表:0000:0000~0000:03FF​

​​

​​

​​

​​

​​

​​

‍

P67 单步中断

​​

​​

​​

‍

P68 由int指令引发的中断

​ ​

​​

​

‍

P69 BIOS和DOS中断处理

​​

​​

​​

​​

​​

​​

‍

P70 端口的读写

​​

​​

​​

​​

​​

‍

P71 操作CMOS RAM芯片

​​

128 个字节的 RAM 中存储:内部实时钟、系统配置信息、相关的程序(用于开机时配置系统信息)。

CMOS RAM 芯片靠电池供电,关机后其内部的实时钟仍可正常工作, RAM 中的信息不丢失。

该芯片内部有两个端口,端口地址为70h和71h,CPU 通过这两个端口读写CMOS RAM:

70h地址端口,存放要访问的CMOS RAM单元的地址;

71h数据端口,存放从选定的单元中读取的数据,或要写入到其中的数据。

读取CMOS RAM的两个步骤:

将要读取的单元地址送入70h地址端口;

从数据端口71h读出指定单元的内容。

​​

​​

‍

P72 外设连接与中断

​​

​​

​​

‍

P73 PC机键盘的处理过程

​​

断码 = 通码+80H

​​

​​

​​

‍

P74 定制键盘输入处理

​​

​​

​​

​​

​​

‍

P78 磁盘读写

​​

​​

​​

​​

​​

‍

‍

‍

‍

‍