Bootstrap

汇编语言基础

引言

     汇编语言是直接在硬件之上工作的编程语言,首先要了解硬件系统的结构,才能有效的应用汇编语言对其编程。汇编课程的研究重点放在如何利用硬件系统的编程结构和指令集有效灵活的控制系统进行工作。

基础知识

1.1机器语言

机器语言是机器指令的集合。
机器指令展开来讲就是一台机器可以正确执行的命令。
指气:01010000(PUSH AX)
电平脉冲 :

早期的程序员们将 0、1 数字编程的程序代码打在纸带或卡片上,1打孔,0不打孔,再将程序通过纸带机或卡片机输入计算机,进行运算

1.2汇编语言的产生

汇编语言的主体是汇编指令。
汇编指令和机器指令的差别在于指令的表示方法上。汇编指令是机器指令便于记忆的书写格式。
汇编指令是机器指令的助记符

机器指令: 1000100111011000
操作 : 寄存器 BX的内容送到AX中
汇编指令 :MOV AX,BX
这样的写法与人类语言接近,便于阅读和记忆

寄存器 : 简单的讲是CPU中可以存储数据的器件,一个CPU中有多个寄存器。AX是其中一个寄存器的代号,BX是另一个寄存器的代号。

计算机能读懂的只有机器指令,那么如何让计管机执行程序员用汇编指令编写的程序呢?

 1.3汇编语言的组成

汇编语言由以下3类组成 :

1、汇编指令(机器码的助记符)(由编译器执行 )

2、伪指令

3、其它符号 (由编译器识别)

汇编语言的核心是汇骗指令,它决定了汇编语言的特性。

1.4存储器

CPU 是计算机的核心部件·它控制整个计算机的运作并进行运算,要想让一个CPU 工作,就必须向它提供指令和数据
指令和数据在存储器中存放,也就是平时所说的内存

在一台PC机中内存的作用仅次于CPU。
离开了内存,性能再好的CPU也无法工作。
磁盘不同于内存,磁盘上的数据或程序如果不读到内存中,就无法被CPU 使用。

(cpu读内存快  内存读硬盘慢)

1.5指令和数据

 指令和数据是应用上的概念。

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

1.6存储单元

存储器被划分为若干个存储单元,每个存储单元从0开始顺序编号 ;

对于大容量的存储器一般还用以下单位来计量容量 (以下用B来代表Byte) :
IKB=1024B  IMB=1024KB IGB=1024MB ITB=1024GB

1.7CPU对存储器的读写

CPU要想进行数据的读写,必须和外部器件 (标准的说法是芯片) 进行三类信息的交互 :
存储单元的地址 (地址信息) 

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

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

 在计算机中专门有连接CPU和其他芯片的导线,通常称为总线。
物理上 : 一根根导线的集合 ;
逻辑上划分为 :
地址总线
数据总线
控制总线

 1.8地址总线

CPU是通过地址总线来指定存储单元的。
地址总线上能传送多少个不同的信息 ,CPU就可以对多少个存储单元进行寻址。

一个CPU有N根地址总线,则可以说这个CPU的地址总线的宽度为N。
这样的CPU最多可以寻找2的N次方个内存单元

1.9数据总线

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

1.10控制总线

CPU对外部器件的控制是通过控制总线来进行的。在这里控制总线是个总称,控制总线是一些不同控制线的集合。有多少根控制总线,就意味着CPU提供了对外部器件的多少种控制。所以,控制总线的宽度决定了CPU做对外部器件的控制能力。
控制总线上发送的控制信息

前面所讲的内存读或写命令是由几根控制线综合发出的 :其中有一根名为读信号输出控制线负责由CPU 向外传送读信号,CPU 向该控制线上输出低电平表示将要读取数据 ;
有一根名为写信号输出控制线负责由CPU后外传送写信号。

1.11内存地址空间

什么是内存地址空间呢?
一个CPU的地址线宽度为10,那么可以寻址1024个内存单元,这1024个可寻到的内存单元就构成这个CPU的内存地址空间。下面深入讨论
首先需要介绍两部分基本知识,主板和接口卡。

1.12主板

在每一台PC机中,都有一个主板,主板上有核心器件和一些主要器件
这些器件通过总线 (地址总线、数据总线、控制总线) 相连。

1.13接口卡

计算机系统中,所有可用程序控制其工作的设备,必须受到CPU的控制。
CPU对外部设备不能直接控制,如显示器、音箱、打印机等。直接控制这些设备进行工作的是插在扩展插槽上的接口卡·

1.14各类存储器芯片

从读写属性上看分为两类 :随机存储器 (RAM) 和只读存储器 (ROM)
从功能和连接上分类 :
随机存储器RAM
口装有BIOS的ROM
接口卡上的RAM
PC机中各类存储器的逻辑连接情况

装有BIOS的ROMBIOS : Basic Input/Output System,基本输入输出系统。
BIOS是由主板和各类接口卡(如: 显卡网卡等) 厂商提供的软件系统,可以通过它利用该硬件设备进行最基本的输入输出。在主板和某些接O卡上插有存储相应BIOS的ROM。
Return

1.15内存地址空间.

上述的那些存储器在物理上是独立的器件。
但是它们在以下两点上相同 :1.都和CPU的总线相连。2、CPU对它们进行读或写的时候都通过控制线发出内存读写命令。

假设,上图中的内存空间地址段分配如下
地址0~7FFFH的32KB空间为主随机存储器的地址空间 ;
地址8000H~9FFFH的8KB空间为显存地址空间 ;

地址A000H~FFFFH的24KB空间为各个ROM的地址空间

内存地址空间
最终运行程序的是CPU 我们用汇编编程的时候,必须要从CPU角度考虑问题。

寄存器(CPU工作原理)

概述

一个典型的CPU由运算器、控制器、寄存器等器件组成,这些器件靠内部总线相连。区别:内部总线实现CPU内部各个器件之间的联系。外部总线实现CPU和主板上其它器件的联系。   

2.1通用寄存器

16位数据在寄存器中的存放情况

数据:20000

二进制表示 :0100111000100000

在寄存器AX中的存储 :

2.2字在寄存器中的存储

一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中。

2.3几条汇编指令

(汇编指令不区分大小写)

汇编指令控制CPU完成的操作用高级语言的描述
mov ax,18将18送入AXAX = 18
mov ah,78将78送入AHAH=78
add ax,8将寄存器AX中的数值加8AH=AX+8
mov ax,bx将寄存器BX中的数据送入寄存器AXAX = BX
add ax,bx将ax,bx中的内容相加,结果存在AX中AX=AX+BX

 例题:

答案:1044CH  但是因为只能存放4位  所以是  044CH

add al,93H 即把93H加到al中   C5+93=158 但是al只能存储两位  所以AX的值为0058H。

这里的丢失,指的是进位制不能在 8 位寄存器中保存,但是 CPU 不是真的丢弃 这个进位值。

2.4物理地址

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

8086有20位地址总线,可传送20位地址,寻址能力为1M。
8086内部为16位结构,它只能传送16位的地址,表现出的寻址能力却只有64K 。

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

2.5段的概念

错误认识 :内存被划分成了一个一个的段,每一个段有一个段地址。
其实:内存并没有分段,段的划分来自于CPU,由于8086CPU用“(段地址X16) +偏移地址=物理地址”的方式给出内存单元的物理地址使得我们可以用分段的方式来管理内存。

(1) 段地址X16 必然是 16的倍数,所以一个段的起始地址也一定是16的倍数 ;
(2) 偏移地址为16位,16 位地址的寻址能力为 64K,所以一个段的长度最大为64K。

CPU访问内存单元时,必须向内存提供内存单元的物理地址。

如果给定一个段地址,仅通过变化偏移地址来进行寻址,最多可以定位多少内存单元了
结论 : 偏移地址16位,变化范围为O~FFFFH,仅用偏移地址来寻址最多可寻64K个内存单元。

2.6段寄存器

 段寄存器就是提供段地址的。8086CPU有4个段寄存器 :Cs、DS、SS、ES
当8086CPU要访问内存时,由这4个段寄存器提供内存单元的段地址。

 cs和ip在地址加法器中得到20000H。进入控制电路通过地址总线进入内存,取到B3 23 01三个数据,即将0123这个值 移动到AX中,此时AX的值为0123H

(I) 从CS:IP指向内存单元读取指令,读取的指会进入指气缓冲器 ;
(2) P = IP+ 所读取指的长度,从而指向下一条指令 ;
(3) 执行指令。 转到步骤(1),重这个过程。

2.7段地址X16+偏移地址=物理地址的本质含义

 2.8代码段

对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。
可以将长度为 N ( N<64KB ) 的一组代码,存在一组地址连续、起始地址为 16的倍数的内存单元中,这段内存是用来存放代码的,从而定义了一个代码段。

CS存放指令的段地址,IP存放指令的偏移地址。
8086机中,任意时刻,CPU将CS:IP指向的内容当作指令执行。

8086CPU的工作过程 :

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

2) IP指向下一条指 ;(3) 执行指令。(转到步骤 (1),重复这个过程。)

2.9段寄存器

段寄存器就是提供段地址的 。8086CPU有4个段寄存器 :CS、DS、SS、ES
当8086CPU要访问内存时,由这4个段寄存器提供内存单元的段地址。

CS和IP是8086CPU中最关键的寄存器它们指示了CPU当前要读取指令的地址
CS为代码段寄存器IP为指令指针寄存器。

2.10CS和IP

在任何时候,CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。
如果说,内存中的一段信息兽被CPU执行过的话,那么,它所在的内存单元必然被CS:IP指向过。

2.11修改CS IP的指令

如何修改AX中的值?

mov 指令
如: mov ax,123
mov指令可以改变8086CPU大部分寄存器的值,被称为传送指令。
能够通过mov 指会改变CS、IP的值吗?

mov指令不能用于设置CS、IP的值,8086CPU没有提供这样的功能。
8086CPU为CS、IP提供了另外的指合来改变它们的值 : 转移指令

同时修改CS、IP的内容 :

jmp 段地址 : 偏移地址

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

仅修改IP的内容:
jmp 某一合法寄存器

jmp ax(类似于 mov IP,ax)
功能: 用寄存器中的值修改IP。

分析下图中CPU运行的流程

初始时 CS=2000H,IP=0000H

执行mov ax,6622H指令,执行完后IP=3 地址为20003H 执行jmp 1000:3

执行后CS=1000H,IP=3H  地址为10003H 执行mov ax,0000 ,执行后IP+3 继续执行下面的

mov bx,ax,执行后IP+2 执行 jmp bx 此时bx=0000  所以跳转到10000H继续执行

2.12代码段

对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。
可以将长度为 N ( N≤64KB ) 的一组代码,存在一组地址连续、起始地址为 16的倍数的内存单元中,这段内存是用来存放代码的,从而定义了一个代码段。

例如:
mov ax,0000  ( B8 00 00 )

add ax,0123   ( 05 23 01 )

mov bx,ax   (8B D8)

jmp bx  (FF E3 )

这段长度为 10 字节的字节的指令,存在从123B0H~123B9H的一组内存单元中,我们就可以认为,123B0H~123B9H这段内存单元是用来存放代码的,是一个代码段 ,它的段地址为123B9H,长度为10字节。

如何使得代码段中的指会被执行呢?

将一段内存当作代码段,仅仅是我们在编程时的一种安排,CPU 并不会由于这种安排,就自动地将我们定义得代码段中的指会当作指会来执行。

实验一

查看CPU和内存,用机器指会和汇编指令编程

R命令查看、改变CPU寄存器的内容 ;

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

E命令改写内存中的内容 ;

U命令将内存中的机器指令翻译成汇编指令
T命令执行一条机器指令 ;

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

 寄存器(内存访问)

3.1内存中字的存储

在0地址处开始存放20000

高位对应高地址位,低位对应低地址位,

问题 :
(1) 0地址单元中存放的字节型数据是多少了?          20H
(2) 0地址字单元中存放的字型数据是多少?            4E20H

(3) 2地址单元中存放的字节型数据是多少? 12H

(4)2地址字单元中存放的字节型数据是多少? 0012H

(5) 1地址字单元中存放的字型数据是多少? 124EH

结论 :
任何两个地址连续的内存单元,N号单元和 N+1号单元,可以将它们看成两个内存单元,也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元。

3.2DS和[address]

CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址 ;在8086PC中,内存地址由段地址和偏移地址组成 。
8086CPU中有一个 DS寄存器,通常用来存放要访问的数据的段地址。

例如: 我们要读取10000H单元的内容可以用如下程序段进行 :
mov bx,1000H
mov ds,bx
mov al.[0]
上面三条指令将10000H (1000:0) 中的数据读到al中。

从哪个内存单元送到哪个寄存器中呢了
mov指令的格式 :   mov 寄存器名,内存单元地址
“[..·]”表示一个内存单元 , “[···]”中的0表示内存单元的偏移地址。
那么内存单元的段地址是多少呢?

执行指令时,8086CPU自动取DS中的数据为内存单元的段地址。如何用mov指令从10000H中读取数据?

执行指令时,8086CPU自动取DS中的数据为内存单元的段地址。如何用mov指令从10000H中读取数据

10000H表示为1000:0 (段地址:偏移地址)将段地址1000H放入ds用mov al.[0]完成传送 (mov指中的说明操作对泉是一个内存单元,[]中的0说明这个内存单元的偏移地址是0,它的段地址默认放在ds中)
如何把1000H送入ds ?

8086CPU不支持将数据直接送入段寄存器的操作,ds是一个段寄存器。

mov ds,1000H 是非法的。
数据→通用寄存器→段寄存器

问题
写几条指令,将al中的数据送入内存单元10000H(思考后分析)
分析问题本质 :怎样将数据从寄存器送入内存单元?

结论:mov bx,1000H

       mov ds,bx

       mov [0],al

3.3字的传送

因为8086CPU是16位结构,有16根数据线,所以,可以一次性传送16位的数据,也就是一次性传送一个字。比如 :
mov bx1000H

mov ds,bx
mov ax,[0];1000:0处的字型数据送入ax

mov [0],cx;cx中的16位数据送到1000:0处

 问题3.3 : 内存中的情况如下图,写出下面指令执行后寄存器ax,bx,cx中的值。

mov ax,10000H ;  执行后  ax=1000H

mov ds,ax;   执行后   ds=1000H

mov ax,[0]; 执行后 ax=1123H

mov bx,[2]; 执行后 bx=6622H

mov cx,[1]; 执行后 cx=2211H

add bx,[1]; 执行后 bx=6622H  +2211H=8833H

add cx,[2]; 执行后 cx=2211H+6622H=8833H

 问题3.4 : 内存中的情况如下图,写出下面指令执行后寄存器ax,bx,cx中的值。

 11316=2C34H

mov ax,11316;执行后 ax=23C4

mov [0],ax;执行后  10001H的值变为23  10000H的值变为C4

mov bx,[0];执行后 bx=23C4H

sub bx,[2];执行后  bx=23C4H-1122H=1B12

mov [2],bx;执行后 10002H的值变为12  10003H的值变为1B

;