《汇编语言(第三版)》笔记
1.1机器语言
1.机器语言:机器指令的合集
2.机器指令:一列二进制数字。
3.计算机将指令转变为相应的一列高低电平,以驱动计算机的电子器件进行运算。 (此处计算机指可以执行机器指令,进行运算的机器。)
4.不同的CPU(Central Processing Unit,中央处理单元,一种微处理器)由于硬件设计和内部结构的不同,由不同的电平脉冲控制,每一种微处理器都有自己的机器指令集(机器语言)
1.2汇编语言的产生
由于机器语言的难于辨别和记忆,汇编语言产生了.
汇编语言是直接在硬件之上工作的编程语言
汇编指令与机器指令的差别在于指令的表达方法,汇编指令是机器指令便于记忆的书写格式 (相当于汇编指令是给人看的,机器指令是给计算机看的,两者呈对应关系)
eg: 将寄存器BX的内容放入AX中
机器指令:1000100111011000
汇编指令:mov ax,bx
寄存器简单来说是在CPU中可以存储数据的器件,一个CPU有多个寄存器
编译器:将汇编指令转换成机器指令的翻译程序
1.3汇编语言的组成
1.汇编指令:机器码的助记符,有对应的机器码
2.伪指令:没有对应的机器码,由编译器执行,计算机并不执行(计算机不认识,编译器认识)
3.其他符号:如 +、-、*、/等,没有对应的机器码,由编译器识别
汇编语言的核心是汇编指令,它决定了汇编语言的特性
1.4存储器
CPU是计算机的核心部件,控制整个计算机的运作并进行运算。
存储器:存放让CPU工作的指令和数据,即我们口中的内存。
在一台PC机中,内存的作用仅次于CPU。离开内存,性能再好的CPU也无法正常工作。就像再聪明的大脑,没有记忆也无法进行思考;性能再好的车,没有车轮也动不了
磁盘信息->内存->程序
1.5指令和数据
指令和数据是应用上的概念
在内存或磁盘上,指令和数据没有任何区别,都是二进制信息。
相同的二进制在不同的地方由不同的意义
eg:
1000100111011000 ->89D8H(数据)
1000100111011000 ->mov ax,bx(程序)
1.6存储单元
存储器被划分成若干个存储单元,每个存储单元从0开始编号
微型存储器的一个存储单元可以存储一个Byte,8个二进制位
微机存储器的容量是以字节为最小单位进行计算的
1Byte=8bit 1TB=2^10GB=2^20MB=2^30KB=2^40B
1.7CPU对存储器的读写
一台微机中不只有存储器一种器件,CPU读取数据时,要指明对哪一个器件进行操作,进行哪种操作;从内存中读数据,要指定存储单元地址
- CPU要想进行数据的读写,必须和外部器件(准确来说是芯片)进行三类信息的交互
- 存储单元的地址(地址信息)
- 器件的选择,读或写命令(控制信息)
- 读或写的数据(数据信息)
CPU通过导线将地址、数据和控制信息传到存储器芯片,在计算机中有专门连接CPU和其他芯片的导线,通常称为总线。
总线:
物理上:一根根导线的合集
逻辑上:地址总线、控制总线、数据总线(根据传送信息的不同划分)
地址总线确定地址->控制总线发送指令->数据总线完成数据的传输
1.8地址总线
- 地址总线上能传送多少个不同的信息,CPU就能对多少个存储单元进行寻址
- 一个CPU有N根地址线,即这个CPU的地址总线的宽度为N。最多可寻找2^N个内存单元
- 地址总线的宽度决定了CPU的寻址能力
1.9数据总线
CPU与内存或其他期间之间的数据传输通过数据总线进行。
数据总线的宽度决定CPU和其他器件进行数据传送时的速度。
eg:8根数据总线一次可传送一个字节(8个二进制数据) 8088CPU :8根 8086CPU:16根
1.10控制总线
- CPU对外部器件的控制是通过控制总线来进行的
- 控制总线是一些不同控制线的合集
- 控制总线的宽度决定CPU对系统中其他器件的控制能力
"读信号输出"控制线负责CPU向外传送读信号,CPU向该控制线上输出低电平表示将要读取数据;
"写信号输出"控制线负责传送写信号
监测点1.1
(1)1个CPU的寻址能力是8KB,那么它的地址总线的宽度为 __13__
8KB=2^13B
一根地址总线对应一个存储单元是1B
N根地址线,地址总线宽度为N,最多可寻2^N个内存单位
N=13
(2)1KB的存储器有1024个存储单元,存储单元的编号从0到1023
(3)1KB的存储器可以存储8192bit,1024Byte
1个存储单元->1B,1KB=1024B 编号从0开始
1B=8bit 1KB=1024*8=8192bit
(4)1GB、1MB、1KB分别是2^30 、2^20、 2^10 Byte
(5)8080、8088、80286、80386的地址总线宽度分别是16根、20根、24根、32根,则他们的寻址能力分别为 64KB、1MB、16MB、4GB
拿8080的16根举例:16根总线,最多可寻2^16个内存单元
2^16 B=2^6KB=64KB
2^20B=1MB
2^24 B=2^4MB=16MB
2^32B=4GB
(6)8080、8088、8086、80286、80386的地址总线宽度分别是8根、8根、16根、16根、32根,则他们一次可以传送的数据为 1B、1B、2B、2B、4B
数据总线8根1B
(7)从内存中读取1024字节的数据,8086至少要读 512次,80386至少要读 256次
8086一次2B,80386一次4B
共1024B
(8)在存储器中,数据和程序以二进制形式存放。
1.11 内存地址空间(概述)
什么是内存地址空间呢?
举例说:一个CPU的地址总线宽度是10,那么可以寻址1024个内存单元,而这1024个可寻到的内存单元就构成这个CPU的内存地址空间
1.12主板
每一台PC机都有一个主板,主板上有核心器件和一些主要器件(CPU,存储器,外围芯片组,扩展插槽等),它们通过总线(地址,数据,控制~)连接。扩展插槽上一般插有RAM内存条和各类接口卡
1.13接口卡
- 计算机系统中,所有可用程序控制器工作设备,必须受到CPU的控制(即CPU–>应用程序–>相应工作设备);
- CPU对外部设备(音响,显示器,打印机等)都不能直接控制
- 直接控制外部设备的是插在扩展插槽上的接口卡
- 扩展插槽通过总线和CPU连接,所以接口卡也通过总线通CPU进行连接
- CPU直接控制接口卡,从而实现CPU对外设的间接控制(CPU通过总线向接口卡发送命令,接口卡根据命令控制外设)
1.14各类存储器芯片
从读写属性上可分为两类:
-
随机存储器(RAM)
-
只读存储器(ROM)
随机存储器可读可写,但必须带电存储,关机后存储的内容丢失;
只读存储器只能读取不能写入,关机后其中的内容不丢失。
从功能和连接上分类:
- 随机存储器RAM
用于存放供CPU使用的绝大部分程序和数据,主随机存储器一般由装在主板上的RAM和插在扩展插槽上的RAM两个位置上的RAM组成
- 装有BIOS(Basic Input/Output System)的ROM
BIOS(基本输入输出系统)是由主板和各类接口卡厂商提供的软件系统,可以通过它利用还硬件设备进行基本的输入输出。在主板和某些接口卡上插有存储相应BIOS的ROM.eg:主板上的ROM存储着主板(系统)的BIOS,网卡上的ROM存储着显卡的BIOS和显卡上的ROM存储着显卡的BIOS
- 接口卡上的RAM
某些接口卡需要对大批量输入、输出数据进行暂时存储,在其上装有RAM。典型的是显卡上的RAM,一般称为显存
1.15内存地址空间
不同存储器在物理上是独立的器件;
但他们都和CPU总线相连;CPU通过控制线对它们进行控制
CPU在操控各存储器时,把他们都当做内存来对待,把他们总的看做一个由若干存储单元组成的逻辑存储器,即我们所说的内存地址空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kvOXoyBR-1594394160271)(C:\Users\Huawei\Desktop\内存地址空间.png)]
1.14各类存储器芯片
从读写属性上可分为两类:
-
随机存储器(RAM)
-
只读存储器(ROM)
随机存储器可读可写,但必须带电存储,关机后存储的内容丢失;
只读存储器只能读取不能写入,关机后其中的内容不丢失。
从功能和连接上分类:
- 随机存储器RAM
用于存放供CPU使用的绝大部分程序和数据,主随机存储器一般由装在主板上的RAM和插在扩展插槽上的RAM两个位置上的RAM组成
- 装有BIOS(Basic Input/Output System)的ROM
BIOS(基本输入输出系统)是由主板和各类接口卡厂商提供的软件系统,可以通过它利用还硬件设备进行基本的输入输出。在主板和某些接口卡上插有存储相应BIOS的ROM.eg:主板上的ROM存储着主板(系统)的BIOS,网卡上的ROM存储着显卡的BIOS和显卡上的ROM存储着显卡的BIOS
- 接口卡上的RAM
某些接口卡需要对大批量输入、输出数据进行暂时存储,在其上装有RAM。典型的是显卡上的RAM,一般称为显存
1.15内存地址空间
- 不同存储器在物理上是独立的器件;
- 但他们都和CPU总线相连;CPU通过控制线对它们进行控制
CPU在操控各存储器时,把他们都当做内存来对待,把他们总的看做一个由若干存储单元组成的逻辑存储器,即我们所说的内存地址空间
图中,所有的物理存储器被看做一个由若干个存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间
第二章 寄存器
典型的CPU由运算器、控制器、寄存器等器件构成
这些器件靠内部总线相连
内部总线实现CPU内部各个器件之间的联系,外部总线实现CPU和主板上其他器件的联系
- 运算器:进行信息处理
- 寄存器:进行信息的储存
- 控制器:控制各种器件进行工作
寄存器是CPU中程序员可以用指令读写的部件,程序员通过改变各种寄存器中的内容来实现对CPU的控制(程序员通过控制寄存器控制CPU)
不同的CPU,寄存器的个数,结构不相同
8086CPU有14个寄存器且都是16位的,可存放两个字节:AX、BX、CX、DX
SI、DI SP、BP、IP CS、SS、DS、ES、PSW
2.1 通用寄存器
通用寄存器:通常用来存放一般性的数据 eg:8086CPU 的AX BX CX DX
一个16位的寄存器能存储的数据最大值是2^16-2=65535
为了兼容,8086CPU的AX BX CX DX这四个寄存器都可分为两个独立使用的8位寄存器来用
AX–>AH和AL; (H=High L=Low)
BX–>BH和BL;
CX–>CH和CL;
DX–>DH和DL;
AX的低八位(0~~7)——>AL;
AX的高八位(8~15)——>AH
2.2字在寄存器中的存储
8086CPU出于对兼容性的考虑,可一次性处理量两种尺寸的数据:
- 字节:byte 1B=8bit
- 字:记为word,一个字两个字节,分别称为字的高位字节和低位字节
2.3 几条汇编指令
- 从后向前运算
- 汇编指令或寄存器名称不区分大小写
问题2.1 执行指令后的AX是?
- 16进制相加满16进一位
由图得知:
8226H+8226H=1044CH
AX为16位寄存器,只能UC南方4位16进制,从右向左取舍
舍去最高位1 ,所以执行指令后的AX是044CH
问题2.2,执行指令后的AX是?
执行add al,93H指令时 AX=00C5H
al指低8位,运算时取AX的C5H, 93H+C5H=158H
al只能存放两位16进制数据,所以高位1舍去(非彻底丢弃),执行指令后的AX=0058H
在进行数据运算或传送时,指令的两个操作对象的位数是一致的;
eg: mov ax ,bx ;
mov ax 18H;
add al 18H;
监测点2.1
(一)
(1) mov ax,62627 AX=F4A3H
62627D=F4A3H
(2)mov ah,31H AX=31A3H
31H覆盖ax中的ah部分 即F4->31
(3)mov al,23H AX=3123H
23H覆盖ax中的al部分,即A3->23
(4)add ax,ax AX=6246H
两个ax相加,3123H+3123H=6246H
(5)mov bx,826CH BX=826CH
826C覆盖bx
(6)mov cx,ax CX=6246H
ax覆盖cx cx=ax=6246H
(7)mov ax,bx AX=826CH
bx覆盖ax,ax=bx=826CH
(8)add ax,bx AX=04D8H
bx与ax相加,放入ax 826CH+826CH=104D8H,最高位1舍去
ax=04D8H
(9)mov al,bh AX=0482H
bh覆盖al,ah不受影响 ax=0482H
(10)mov ah,bl AX=6C82H
bl覆盖ah,al不受影响,ax=6C82H
(11)add ah,ah AX=D882H
两个ah相加,al不受影响
6CH+6CH=D8H ax=D882H
(12)add al,6 AX=D888H
6加到al上,ah不受影响 ax=D888H
(13)add al,al AX=D810H
两个al相加,ah不受影响 88H+88H=110H 高位1舍去
ax=D810H
(14)mov ax,cx AX=6246H
cx覆盖ax ax=cx=6246H
2.4物理地址
我们知道,CPU访问内存单元时,要给出内存单元的地址。
所有的内存单元构成的存储空间是一个一维的线性空间,每一个
内存单元在这个空间中都有唯一的地址,我们将这唯一的地址
称为物理地址。
-
CPU通过地址总线送入存储器的地址必须是一个内存单元的物理地址;
-
在CPU向地址总线上发送物理地址之前,必须要在内部先形成这个物理地址;
-
不同的CPU可以有不同的形成物理地址的方式。
2.5 16位结构的CPU
在8086CPU之前的CPU都是8位机(相当于8086是分割线);8086CPU是16位结构的CPU(16位机)
概括地讲,16位结构(16位机、字长为16位)概述了一个CPU几个方面的结构特性:
- 运算器一次最多可以处理16位的数据;
- 寄存器的最大宽度是16位;
- 寄存器和运算器之间的通路为16位;
8086是16位结构的CPU,也就是说,在8086内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位的。内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放。对于16位CPU,能一次性处理、传输、暂时存储16位的地址。
2.6 8086CPU给出物理地址的方法
8086CPU有20位地址总线,可以传送20位地址,但它又是16位结构,在内部一次性处理、传输、暂时存储的地址是16位,从CPU内部结构来看,如果将地址从内部简单地发出,它只能送出16位的地址,寻址能力只有64KB
8086CPU内部使用地址加法器将两个16位地址合成一个20位物理地址
- CPU中的相关部件提供两个16位地址,一个段地址,一个偏移地址
- 段地址和偏移地址通过内部总线送入到地址加法器
- 地址加法器将两个16位地址合成一个20位物理地址
- 地址加法器通过内部总线将20位物理地址送入到输入输出控制电路
- 20位物理地址被地址总线传送到存储器
地址加法器采用物理地址=段地址×16+偏移地址方法合成20位的物理地址
例如8086CPU要访问地址为123C8H的内存单元,其工作过程:
段地址×16通俗说就是向左移四位(二进制位),十六进制就是移一位
- 一个数据的十六进制形式移一位等同于乘以16,十进制左移一位就是乘以10,···一个X进制的数据向左移一位,相当于乘以X,向左移几位,就是乘以X的几次方
2.7 “段地址×16+偏移地址=物理地址”的本质含义
“段地址×16+偏移地址=物理地址”的本质含义:CPU在访问内存时,用一个基础地址(段地址×16)和一个相对基础地址的偏移地址相加,给出内存单元的物理地址
更一般的说,8086CPU的这种寻址功能是“基础地址+偏移地址=物理地址寻址模式的一种具体实现方案。
举例说明基础地址+偏移地址=物理地址思想
①从学校去图书馆的路:
(1)从学校走2826米到图书馆,2826米可看做图书馆的物理地址
(2)从学校走2000米到体育馆,从体育馆走826米到图书馆,第一个距离2000米是相对学校的基础地址,826米是相对基础地址的偏移地址
②通过纸张相互通信,图书馆的地址只能写在纸上,现在仅有两张可以容纳三位数据的纸条。
第一张纸上写200(段地址),第二张纸826(偏移地址)。通过公式做运算:200×10+826=2826(物理地址)
8086CPU就是这样一个只能提供两张3位数据纸张的CPU
2.8段的概念
内存中并没有分段,段的存在是人为根据需求进行划分的,便于执行操作。在编程时可以根据需要,将若干地址连续的内存单元看做一个段,用段地址×16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。
- 段地址×16必然是16的倍数
- 偏移地址为16位,16位地址的寻址能力为64KB,所以一个段的长度最大为64KB
(1) CPU可用不同的段地址和偏移地址形成同一个物理地址
eg:
物理地址 段地址 偏移地址 21F60H 2000H 1F60H 2100H 0F60H (2)如果给定一个地址段(SA),仅通过变化偏移地址(EA)来进行寻址,最多可定位多上个内存单元?
偏移地址为16位,变化范围为0~FFFFH,仅用偏移地址来寻址最多可寻64KB个内存单元
必如给定段地址1000H,用偏移地址寻址,CPU的寻址范围为:10000H~1FFFFH。
检测点2.2
(1)给定段地址0001H,仅通过偏移地址寻址,CPU的寻址范围为0010H到1000FH
SA*10H+EA=物理地址
SA*10H+(0H~FFFFH)=物理地址
l:0010H+0H=0010H
h:0010H+FFFFH=1000FH
(2)有一段数据存放在内存20000H单元中,现给定段地址为SA,若想用偏移地址寻到此单元。则SA应满足的条件是:最小为1001H最大为20000H
SA*10H+EA=物理地址=20000H
EA:(0H~FFFFH)
SAh=(20000H-0)/10H=2000H
SAl=(20000H-FFFFH)/10H=1000H 验证:1000H*10+FFFFH=1FFFFH<20000H
所以SAl=1001H
2.9 段寄存器
**段寄存器:提供段地址的部件,段地址在段寄存器中存放。8086CPU中由4个寄存器:CS、DS、SS、ES。**8086CPU访问内存时由这四个寄存器提供内存单元的段地址
2.10 CS和IP
CS和IP是8086CPU中两个最关键的寄存器,指示COU当前要读取指令的地址。
CS是代码段寄存器;IP是指令指针寄存器
在8086机中,任意时刻,CPU将CS:IP指向的内容当做指令执行
(1)CPU当前状态:CS:2000H IP:0000H
(2)内存20000H~20009H单元存放可执行的机器码
(3)机器码对应指令:
地址:20000H~20002H 内容:B8 23 01 长度:3Byte 对应汇编指令 mov ax,0123H
地址:20003H~20005H 内容:BB 03 00 长度:3Byte 对应汇编指令 mov bx,0003H
地址:20006H~20007H 内容:89 D8 长度:2Byte 对应汇编指令 mov ax,bx
地址:20008H~20009H 内容:01 D8 长度:2Byte 对应汇编指令 add ax,bx
指令执行过程:
结合上图:三个步骤进行循环
-
从CS:IP指向的内存单元读取指令,读取的指令进入指令缓存器;
-
IP=IP+所读取指令的长度,从而指向吓一跳指令;
-
执行指令,重复步骤1,2.
CPU根据什么将内存中的信息看作指令?
CPU将CS:IP指向的内存单元中的内容看做指令。因为在任意时刻,CPU都将CS、IP中的内容当做指令的段地址和偏移地址。如果说,内存中的一段地信息曾被CPU执行过,那么,他所在的内存单元必然被CS:IP指向过。
2.11 修改CS、IP指令
在CPU中,程序员能用指令读写的部件只有寄存器。通过改变寄存器中的内容实现对CPU的控制。
通过改变CS、IP中的内容来控制CPU执行目标指令
mov指令被称为传送指令 ,eg:mov bx,123将bx的值设为123;CPU中大部分寄存器的值,都可用mov命令改变,但不能改变CS、IP值
转移指令 :能够改变CS、IP的内容的指令,jmp是其中最简单的指令
“jum 段地址:偏移地址”功能:将指令中给出的段地址修改CS,偏移地址修改IP
eg:jmp 2AE3:3 执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令
jmp 3:0B16 执行后:CS=0003H,IP=0B16H,CPU将从0B46H处读取指令
仅修改IP内容:jmp 某一合法寄存器 功能:用寄存器中的值修改IP,jmp ax==mov IP,ax(不真实存在,便于理解产生)
eg:jmp ax,指令执行前:ax=1000H,CS=2000H,IP=0003H
指令执行后:ax=1000H,CS=2000H,IP=1000H
jmp bx,指令执行前:bx=0B16H,CS=2000H,IP=0003H
指令执行后:bx=0B16H,CS=2000H,IP=0B16H
问题2.3
内存中存放的机器码和对应的汇编指令情况如图,设CPU初始状态:CS=2000H,IP=0000H,请写出指令执行序列。
(1)CS=2000H,IP=0000H,CPU从内存中20000H处读数据,读入指令:B8 22 66(mov ax,6622H) ax=6622H
(2)CS=2000H,IP=0003H CPU从内存中20003H处读数据,读入指令:EA 03 00 00 10(jmp 1000:3) 执行后CS=1000H,IP=0003H
(3)CS=1000H,IP=0003H,CPU从内存中10003H处读数据,读入指令:B8 00 00(mov ax,0000H) ax=0000H
(4)CS=1000H,IP=0006H,CPU从内存中10006H处读数据,读入指令:8B D8(mov bx,ax) bx=ax=0000H
(5)CS=1000H,IP=0008H,CPU从内存中10008H处读数据,读入指令:FF E3(jmp bx) 执行后 IP=0000H
(6)CS=1000H.IP=0000H,CPU从内存中10000处读取地址,读入指令:B8 23 01(mov ax,0123H) IP=IP+3=0003H…
执行序列:
(1)mov ax,6622H
(2)jmp 1000:3
(3)mov ax,0000
(4)mov bx,ax
(5)jmp bx
(6)mov ax,0123H
(7)回到(3)…
2.12代码段
eg:mov ax,0000 (B8 00 00)
add ax,0123H (05 23 01)
mov bx,ax (8B D8)
jmp bx (FF E3)
这段长度为10个字节的指令,存放在123B0H123B9H的一组内存单元中,就可以认为,123B0H123B9H这段内存是用来存放代码的,是一个代码段,他的段地址是123BH,长度为10字节。如果想让这段代码得到执行,课设CS=123BH,IP=0000H。(将CS:IP指向定义代码段的第一条指令的首地址)
监测点2.3
下面的三条指令后,CPU几次修改IP?都是在什么时候?最后IP中的值是多少?
mov ax,bx
sub ax,ax
jmp ax
mov ax,bx 修改1次,执行,ax=bx
sub ax,ax 修改1次 执行,ax=0
sub=subtract减 sub ax,ax ->ax=ax-ax=0/(左=左-右)
jmp ax 修改1次,执行 修改一次 IP=ax=0
所以最后CPU修改四次,IP=0
实验一 查看CPU和内存,用机器指令和汇编指令编程
(1)debug功能
- R命令:查看、修改CPU寄存器的内容
- D:查看内存中的内容
- E:改写内存中的内容
- U:将内存中的机器指令翻译成汇编指令
- T:执行CS:IP指向的内存处的指令
- A:以汇编指令的格式在内存中写入一条机器指令
(2)本次实验在DOSbox中进行
(3)实验内容:
…(-t 运行下去)
2^8=256 256D=100H
-t…
不能修改,ROM(read-only memory)是只读存储器
第三章 寄存器(内存访问)
3.1 内存中字的存储
字单元:存放一个字形数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字形数据的高位字节,低地址内存单元中存放字形数据的低位字节。
将起始地址为N的字单元简称为N地址字单元。eg:一个字单元由2,3两个内存单元组成,则可以说这是2地址字单元
问题3.1
对于图3.1:
(1)0地址单元中存放的字节型数据是多少?
20H
(2)0地址字单元中存放的字形数据是多少?
4E20H
(3)2地址单元中存放的字节型数据是多少?
12H
(4)2地址字单元中存放的字形数据是多少?
0012H
(5)1地址字单元中存放的字形数据是多少?
由1号单元和2号单元组成,2号高8位,1号低8位
124EH 大小:4686
3.2 DS和[address]
DS寄存器:段寄存器.通常用来存放要访问数据的段地址
比如读取10000H单元的内容:
mov bx,1000H 将段地址放入通用寄存器bx中
mov ds,bx 令段寄存器ds=bx
8080CPU不支持直接将数据送入段寄存器;硬件设计问题导致
mov al,[0] 将内存单元的偏移地址0传送
mov命令:
- 将数据直接送入寄存器;eg mov ax,1000H
- 将一个寄存器内容送入另一个寄存器; eg:mov ax,bx
- 将一个内存单元中的内容送入一个寄存器中;eg:mov al,[0] 格式:mov 寄存器名,内存单元地址
“[···]”表示一个内存单元,“[···]”中的0表示内存单元的偏移地址
段地址+偏移地址->内存地址
内存单元的段地址在哪里找呢?
- 指令执行时,8086CPU自动取ds中的数据为内存地址中的段地址
问题3.2
写几条指令,将al中的数据送入内存单元10000H中
10000H(1000:0)
与文中示例相反 mov [0],al
所以流程:
mov bx,1000H
mov ds,bx
mov [0],al
3.3字的传送
8086CPU是16位结构,有16根数据线,所以可以一次性传送16位数据,一个字。
mov bx,1000H
mov ds,bx
mov ax,[0] 将1000:0处的字形数据送入ax(16位)中
mov [0],cx cx中16位数据送到1000:0处
问题3.3
内存中情况如图所示,写出下面的指令执行后寄存器中ax,bx,cx中的值
mov ax,1000H ax=1000H
mov ds,ax ds=1000H
mov ax,[0] ax=1123H 1000:0处的两个单元的 数据放入ax
mov bx,[2] bx=6622H
mov cx,[1] cx=2211H
add bx,[1] bx=bx+2211H=8833H
add cx,[2] cx=cx+6622H=8833H
问题3.4
11316=2C34H
mov ax 11316 ax=2C34H 10000H 34; 10001H 2C
mov [0],ax 1000:0 34;1000:1 2C
mov bx,[0] bx=2C34H
sub bx,[2] bx=bx-1122H=1b12H
mov [2],bx 1000:2 12; 1000:3 1B
3.4 mov、add、sub指令
已知mov命令:
mov 寄存器,数据 eg:mov ax,8
mov 寄存器,寄存器 eg:mov ax,bx
mov 寄存器,内存单元 eg:mov ax,[0]
mov 内存单元,寄存器 eg:mov [0],ax
mov 段寄存器,寄存器 eg:mov ds,ax
推倒测试成功mov命令:
mov 寄存器,段寄存器
eg:mov ax,ds
mov 内存单元,段寄存器
eg:mov ax,1000
mov ds,ax
mov [0],ds
mov 段寄存器,内存单元
eg:mov ax,1000
mov ds,ax
mov ds,[0]
add和sub指令同mov一样,都有两个操作对象
它们不能对段寄存器进行操作;eg:add ds,ax 非法
在mov add sub jmp四个指令中,只有jmp是具有一个操作对象的指令
3.5数据段
段的含义参照2.8节
可认为123B0H~123B9H这段内存是一个数据段,段地址为123B,长度为10个字节。
如何访问数据段中的数据呢?
在具体操作的时候,用ds存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元
eg:将123B0H~123B9H的内存单元定义为数据段,累加数据段中的前3个单元中的数据
mov ax,123B
mov ds,ax 数据段段地址123BH
mov al,0 用al存放累加结果
add al,[0]
add al,[1]
add al,[2]
问题3.5
add ax,[0] 将[0],[1] (各8位)中的内容加入ax(16位)中
add ax,[2] 将[2],[3]中的内容加入ax中
add ax,[4] 将[4],[5]中的内容加入ax中
监测点3.1
mov ax,1 ax=1
mov ds,ax ds=ax=1H
mov ax,[0000] 内存地址=段地址*10H+偏移地址=10H ax=2662H
mov bx,[0001] bx=E626H
mov ax,bx ax=bx=E626H
mov ax,[0000] ax=2662Hmov bx,[0001] bx=D6E6H
add ax,bx ax=FD48H
add ax,[0004] ax=2C14H
mov ax,0 ax=0H
mov al,[0002] ax=00E6Hmov bx,0 bx=0H
mov bl,[000C] bx=0026H
add al,bl ax=000CH
CS=2000,IP=0 指向内存地址:2000H
mov ax,6622H ax=6622H CS=2000H,IP=0003H
jmp 0ff0:0100 跳转指令 CS=2000,IP=0008H
指令执行后CS=0FF0H,IP=0100H 内存地址:10000H
mov ax,2000 ax=2000H CS=0FF0H,IP=0103H
mov ds,ax ds=ax=2000H CS=0FF0H,IP=0105H
mov ax,[0008] 此时段地址ds=2000H 偏移地址0008,指向20008位置 ax=C389H CS=0FF0,IP=0108
mov ax,[0002] 指向内存地址20002H ax=EA66H
CS=0FF0H,IP=010BH
3.6栈
栈是一种具有特殊的访问方式的存储空间
特点:先进后出,后进先出 (LIFO Last In First Out) 参照数据结构
栈的两个基本操作:出栈和入栈
3.7CPU提供的栈机制
8086CPU提供入栈和出栈指令(push入栈 pop出栈),出入栈操作都是以字为单位进行
push ax 将寄存器ax中的数据送入栈中
pop bx 将从栈顶取出数据放入bx
通过段寄存器SS和寄存器SP存放栈顶的地址,栈顶的段地址存放在段寄存器SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素
pop 出栈:
(1)将SS:SP指向的内存单元处的数据送入ax
(2)SP=SP+2,箭头向下移两个字节,SS:SP指向当前栈顶下面的单元,成为新的栈顶
问题3.6
如果将10000H~1000FH这段空间当做栈,初始状态栈是空的,此时SS=1000H,SP=?
栈自下向上存放数据
栈空,SS:SP指向栈空间最高地址单元的下一个单元
解析:栈空间大小:16个字节,栈底SS:SP=1000:000E
当栈中只有一个元素时,SS:SP指向1000:000E,最后一个元素出栈,出栈后,SP=SP+2=000EH+2H=0010H
所以SP=0010H
3.8栈顶超界的问题
栈顶超界是极其危险的,可能会造成一连串的错误;
CPU不保证我们对栈的操作不会超界,8086CPU只知道栈顶在哪,但不知道栈有多大,即不知道自己指向的栈顶是否已超出栈的范围;8086CPU的工作机制:只考虑当前情况,当前的栈顶在何处,当前要执行的指令是哪一条。
在编程时,自行操心栈顶超界的问题(包括出栈和入栈)
push入栈超界:
pop出栈超界:
3.9 push 、pop指令
push、pop指令格式:
寄存器:
- push 寄存器 ;将一个寄存器中的数据入栈
- pop 寄存器 ;将出栈的数据放入一个寄存器中
段寄存器:
- push 段寄存器 ;将一个段寄存器中的数据入栈
- pop 段寄存器 ;将出栈的数据放入一个段寄存器中
内存单元之间:
- push 内存单元 ;将一个内存字单元处的字入栈(栈操作都是以字为单元)
- pop 内存单元 ;用一个内存子单元接收出战的数据
eg:
mov ax,1000H
mov ds,ax 确定段地址
push [0]; 将1000:0处的数据放入栈中
pop [2]; 将出栈的数据放入1000:2处
指令执行时,CPU要知道内存单元的地址,可以在push,pop指令中只给出内存单元的偏移地址,段地址在ds中得到。
push指令执行步骤:
- SP=SP-2;
- 向SS:SP指向的字单元放入数据
pop指令执行步骤:
- 从SS:SP指向的字单元中读数据;
- SP=SP+2
问题3.7
编程,将10000H~1000FH这段空间当做栈,初始状态是空,将AX,BX,DS中的数据入栈。
指定栈顶位置
mov ax,1000H
mov ss,ax
mov sp,0010H 栈此时为空,sp=sp+2=000FH+2=0010H
将AX,BX,DS中的数据入栈
push ax
push bx
push ds
问题3.8
编程:
(1)将10000H~1000FH这段空间当做栈,初始状态栈是空的;
(2)设置AX=001AH,BX=001BH;
(3)将AX,BX中的数据入栈;
(4)然后将AX,BX清零;
(5)从栈中回复AX,BX原来的内容。
ax先进,bx先出
问题3.9
编程:
(1)将10000H~1000FH这段空间当做栈,初始状态栈是空的;
(2)设置AX=001AH,BX=001BH;
(3)利用栈,交换AX和BX中的数据。
问题3.10
如果要在10000H处写入字形数据2266H,可以用以下代码完成:
mov ax,1000H
mov ds,ax
mov ax,2266H
mov [0],ax
补全下面的代码,使它能够完成相同的功能:在10000H处写入字形数据2266H。
要求:不能使用“mov 内存单元,寄存器”这类指令
使用push指令代替mov [0],ax
mov ax,1000
mov ss,ax
mov sp,0002
mov ax,2266
push ax
mov与pop,push指令不同;CPU执行mov指令只需一步操作,就是传送;执行push,pop指令需两步: push:先改变SP,后向SS:SP处传送(先说再给); pop:先读取SS:SP处的数据,后改变SP(先拿再说)
push,pop等栈操作指令,修改的只是SP,即栈顶的变化范围:0~FFFFH
3.10栈段
将一组地址连续、起始地址为16的倍数的内存单元当做栈空间来用,从而定义了一个栈段
如何使用栈段?
- 将SS:SP指向我们定义的栈段即可
问题3.11
如果将10000H~1FFFFH这段空间当做栈段,初始状态栈是空的,此时,SS=1000H,SP=?
栈中只有一个元素是时SS:SP=1000:FFFE
栈空时SP=SP+2=0H
所以SP=0
问题3.12
一个栈段最大可以设为多少?为什么?
栈段范围大小由SP决定
SP:0~FFFFH
一个栈段容量最大为64KB
段的综述
我们可以称放数据的段为数据段;放代码的段为代码段;将一个当栈的段成为栈段
数据段:段地址在DS中,mov,add,sub等常用指令
代码段:段地址在CS中,偏移地址在IP中,mov,add,sub等常用指令
栈段:段地址在SS中,偏移地址在SP中,push,pop等常见操作指令
监测点3.2
mov ax,1000
mov ds,ax
mov ax,2000 //设置栈
mov ss,ax
mov sp,0010 //从栈底开始
push [0] //push ds:[0] 10000处的0123放到栈中
push [2]
push [4]
push [6]
push [8]
push [A]
push [C]
push [E]
(2)补全下面的程序,使其可以将10000H1000FH中的8个字,逆序复制到200002000F中。
mov ax,2000
mov ds,ax
mov ax,1000 //设置栈
mov ss,ax
mov sp,0
mov sp,
pop [E] //pop ds:[E] 10000H的0123放到2000E处
pop [C]
pop [A]
pop [8]
pop [6]
pop [4]
pop [2]
pop [0]
实验二 用机器指令和汇编指令编程
(一)D命令:
d 段地址:偏移地址 //实验一中段地址和偏移地址直接给出
d 段寄存器:偏移地址
CPU访问内存单元时从段寄存器中获得内存单元的段地址
debug在处理D命令的程序段中,必须有将段地址送入段寄存器的代码
段寄存器有4个:CS,SS,DS,ES
将段地址放入哪个段寄存器呢?
CS:IP必须指向debug处理D命令的代码;
SS:IP指向栈顶;
所以CS,SS都不行
那么只剩DS和ES。
我们知道,访问内存的指令如:mov ax,[0] 是默认段地址在ds,所以debug在处理“d 段地址:偏移地址” 这种D命令时,将段地址送入ds中比较方便
d 段寄存器:偏移地址示例:
(二)在E、A、U命令中使用段寄存器
方法同d命令,示例:
实验任务
执行1:
执行二:
执行三:push ax
mov ax,[0] ax=C0EAH
add ax,[2] ax=C0EFH
mov bx,[4] bx=30F0H
add bx,[6] bx=6021H
push ax sp:00FE 修改的内存单元地址是:2200:00FE 内容:C0FCH
push bx sp:00FC 修改的内存单元地址是:2200:00FC 内容:6021H
pop ax sp:00FE ax=6021H
pop bx sp:0100 bx=C0FCH
push [4] sp:00FE 修改的内存单元地址是:2200:00FE 内容:30F0H
push [6] sp:00FC 修改的内存单元地址是:2200:00FC 内容:2F31H
2.仔细观察图中实验,然后分析,为什么2000:0~2000:f中的内容会发生改变
每执行一条t指令,就回将寄存器保存到栈中,栈临时保存数据(不是很理解)
第四章 第一个程序
4.1 一个源程序从写出到执行的过程
第一步:编写汇编源程序 -> 产生一个存储源程序的文本文件
第二步:对源程序进行编译连接 ->产生一个可在操作系统运行的可执行文件
先用汇编语言编译程序对源程序文件中的源程序进行编译,产生目标文件;
再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件
可执行文件(.exe)包括:
- 程序(从源程序中的汇编指令翻译过来的机器码)和数据(源程序中定义的数据)
- 相关的描述信息(比如,程序有多大,要占用多少内存…)
第三步:执行可执行文件中的程序
4.2源程序
1.伪指令
汇编语言源程序中包括两种指令,汇编指令和伪指令;汇编指令是有对应的机器码的指令,可被编译为机器指令,最终由CPU执行;伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作
程序4.1中
(1) XXX segment
.
.
.
XXX ends
segment和ends是一对成对使用的伪指令,写可被编译的汇编程序时必用的一对伪指令;segment和ends的功能是定义一个段,segment表示一个段的开始,ends表明一个段的结束,XXX是定义的段名
段名 segment
…
段名 ends
eg:程序4.1中的 codesg segment 段名是codesg
codesg ends
一个汇编程序由多个段组成,这些段被用来存放代码、数据或当做栈空间来使用。
(2)end
end是一个汇编程序的结束标记,是整个程序的结束
(3)assume
假设;它假设某一段寄存器和程序中的某一个用segment…ends定义的段(/有特定用途的段)相关联;
程序4.1中 assume cs:codesg 就是将CPU中的段寄存器cs与段codesg联系起来
2.源程序中的“程序”
这里的“程序”值得是源程序中最终由计算机执行、处理的指令或数据
以后将源程序文件中所有内容成为源程序;源程序中最终由计算机执行、处理的指令或数据成为程序
3.标号
例如 “codesg”,一个标号代指了一个地址
codesg在segment前面,作为一个段的名称,这个段的名称最终将被编译、连接程序处理为一个段的段地址
4.程序的结构
- 定义一个段,名称为abc abc segment…abc ends
- 在断种写入汇编指令, mov ,add…
- 指出程序在何处结束 end
- abc被当做代码段来用,将abc和cs联系起来 assume cs:abc
程序4.2
assume cs:abc
abc segment
mov ax,2
add ax,ax
add ax,ax
abc ends
end
5.程序返回
一个程序P2向运行,必须有一个正在运行的程序P1将其加载到内存中,将CPU的控制权交给P2,P2才能运行,P2开始运行,P1暂停,P2结束后,将CPU的控制权交还给P1,P1继续运行。
一个程序结束后,将CPU的控制权交还给使他得以运行的程序,这个过程称为:程序返回。通过在程序的末尾添加返回的程序段进行返回
mov ax,4c00H
int 21H
程序4.1中
mov ax,4c00H
int 21H
实现程序返回
6.语法错误和逻辑错误
**当程序没有返回时,编译是不报错的,**对编译器来说程序4.2是正确的程序。
程序在编译时报错是语法错误
运行时发生的错误是逻辑错误