编程语言 / 计算机技术 · 2021年4月8日 0

跟小甲鱼学汇编(一)——基础知识与寄存器

前言

跟小甲鱼去入门一下汇编语言

 

注:小甲鱼的这系列课程讲的有点乱、比较浅,新手可以入个门,老手就别跟这个教程了。


引言

汇编语言是直接在硬件之上工作的编程语言,首先要了解硬件系统的结构,才能有效的应用汇编语言对其进行编程。

汇编课程的研究重点放在如何利用硬件系统的编程结构和指令集有效灵活的控制系统进行工作。

我们的目的是去学会“机器思维”

 


背景

机器语言

机器语言是机器指令的集合,机器语言展开来讲就是一台机器可以正确执行的命令。

机器语言就是最底层的语言,也就是密密麻麻的0和1。

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

汇编语言的产生

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

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

汇编指令是机器指令的助记符。

例如:

机器指令:1000100111011000
操作:寄存器BX的内容送到AX中
汇编指令:MOV AX,BX

这样的写法与人类语言接近,便于阅读和记忆

 

 


进入汇编与了解计算机

汇编语言的组成

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

  1. 汇编指令(机器码的助记符)
  2. 伪指令(由编译器执行)
  3. 其它符号(由编译器识别)

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

 

存储器

CPU是计算机的核心部件,它控制整个计算机的运作并进行运算,要想让一个CPU工作,就必须向它提供指令和数据。

指令和数据在存储器中存放,也就是平时所说的内存。

磁盘不同于内存,磁盘上的数据或程序如果不读到内存中,就无法被CPU使用。

 

指令和数据

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

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

二进制信息:

1000100111011000—->89D8H(数据)

1000100111011000—->MOV  AX,BX(程序)

我们后面会控制电脑去识别成数据或者是程序。

 

存储单元

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

 

对于大容量的存储器一般还用以下的单位来计量容量(以下用B来表示Byte):

  • 1KB=1024B
  • 1MB=1024KB
  • 1GB=1024MB
  • 1TB=1024GB

磁盘的容量单位同内存的一样,实际上以上单位是微机中常用的计量单位。

 

CPU对存储器的读写(重点)

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

  • 存储单元的地址(地址信息
  • 器件的选择,读或写命令(控制信息
  • 读或写的数据(数据信息

电子计算机能处理、传输的信息都是电信号,电信号当然要用导线传送,CPU利用导线传送电信号来进行地址、控制、数据信息的传输。

在计算机中专门有连接CPU和其他芯片的导线,通常称为总线。

总线:

  • 物理上:一根根导线的集合
  • 逻辑上:
    • 地址总线
    • 控制总线
    • 数据总线

 

 

 


CPU三大线

地址总线

CPU是通过地址总线来指定存储单元的。

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

一个CPU有N根地址总线,则可以说这个CPU的地址总线宽度为N。

这样CPU最多可以寻找2的N次方个内存单元。

数据总线

CPU与内存或其他器件之间的数据传送是通过数据总线来进行的。

数据总线的宽度决定了CPU和外接的数据传送速度。

 

控制总线

CPU对外部器件的控制是通过控制总线来进行的。在这里控制总线是个总称,控制总线是一些不同控制线的集合。

有多少根控制总线,就意味着CPU提供了对外部器件的多少种控制。

所以,控制总线的宽度决定了CPU对外部器件的控制能力。

 

16位结构的CPU

这里补充一个位的概念:

64位的CPU,即意义是CPU一次能够传送、处理64位的二进制数,所以理论上它能够寻址的范围是2的64次方,即16777216T。

另外,要实现真正意义上的64位计算,光有64位的处理器是不行的,还必须得有64位的操作系统以及64位的应用软件才行,三者缺一不可,缺少其中任何一种要素都是无法实现64位计算的。

概括的讲,16位结构描述了一个CPU具有以下几个方面的特征:

  1. 运算器一次最多可以处理16位数据
  2. 寄存器的最大宽度为16位
  3. 寄存器和运算器之间的通路是16位

 

 

 


关于计算机的补充知识

内存地址空间

一个CPU的地址线宽度为10,那么可以寻址1024个内存单元,这1024个可寻到的内存单元就构成了这个CPU的内存地址空间。

 

物理地址

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

 

主板

在每一台PC机中,都有一个主板,主板上有核心器件和一些主要器件。

这些器件通过总线(地址总线,数据总线,控制总线)相连。

 

接口卡

计算机系统中,所有可用程序控制其工作的设备,必须受到CPU的控制。

CPU对外部设备不能直接控制,如显示器、音箱、打印机等。直接控制这些设备进行工作的是插在扩展插槽上的接口卡。

 

各类存储器的芯片

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

BIOS:Basic Input/Output System,基本输入输出系统。

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

PC机中各类存储器的逻辑连接情况:

 

补充

上述的那些存储器在物理上是独立的器件。但是他们在以下两点上相同:

  1. 都和CPU的总线相连。
  2. CPU对它们进行读或写的时候都通过控制线发出内存读写命令。

 

 

我们的课程主要是跟随8086CPU去学习,很经典的CPU。

 

 


CPU与寄存器

CPU概述

一个典型的CPU由运算器、控制器、寄存器等器件组成,这些器件靠内部总线相连。

内外总线的区别:

  1. 内部总线实现CPU内部各个器件之间的联系
  2. 外部总线实现CPU和主板上其他器件的联系

 

寄存器概述

寄存器就是在设备中临时存放数据的东西。寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。

8086CPU有14个寄存器,它们的名称为:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。

8086CPU所有的寄存器都是16位的,可以存放两个字节。

 

通用寄存器

AX、BX、CX、DX通常用来存放一般性的数据,被称为通用寄存器

 

下面以AX为例,我们看一下寄存器的逻辑结构,下面是AX寄存器存储一个18(10010):

16位寄存器可以存储的数据的最大数值是二的十六次方减一,可以存储的数有二的十六次方个数。

 

8086上一代CPU中的寄存器都是8位的,为保证兼容性,这四个寄存器都可以分为两个独立的8位寄存器使用。

例如AX就可以分为AH和AL(H:高;L:低),AX的低八位构成了AL寄存器、AX的高八位都成了AH寄存器。BX、CX、DX相仿。

 

字在寄存器中的存储

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

不管你怎么分析内存空间,在CPU的角度上,内存空间都是一个一维的排列。

 

8086 CPU给出物理地址的方法

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

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

8086CPU为了高效利用地址总线,采用了一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。

即当8086读写内存时,发生了这么一些事:

  1. CPU中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址
  2. 段地址和偏移地址通过内部总线送入一个称为地址加法器的部件
  3. 地址加法器将两个16位地址合并成一个20位的地址

 

地址加法器工作原理

地址加法器合成物理地址的方法:

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

乘以16对十六进制的数来说,就是直接向右移动一位。

 

段的概念

错误认识:内存被划分成了一个一个的段,每一个段有一个段地址

真正理解:内存没有被分段,段的划分来自于CPU,由于8086CPU用“(段地址*16)+偏移地址=物理地址”的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。

以后,在编程时可以根据需要,将若干地址连续的内存单元看做一个段,用段地址*16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。

 

三点需要注意:

  1. 段地址*16必然是16的倍数,所以一个段的起始地址也一定是16的倍数。
  2. 偏移地址为16位,16位地址的寻址能力是64k,所以一个段的长度最大为64K
  3. CPU可以通过不同的段地址和偏移地址形成同一个物理地址(例如:2000和1F60、2100和0F60都以同一个物理地址)

 

另外,对于8086PC机的两种描述:

  1. 数据存在内存2000:1F60单元中
  2. 数据存在内存2000段中的1F60H单元中

 

段寄存器

段寄存器就是提供段地址的寄存器。

8086CPU有4个段寄存器:CS、DS、SS、ES

当8086CPU要访问内存时,由这4个段寄存器提供内存单元的地址。

CS (Code Segment):代码段寄存器;
DS (Data Segment):数据段寄存器;
SS (Stack Segment):堆栈段寄存器;
ES (Extra Segment):附加段寄存器;

 

CS和IP

CS:代码段寄存器
IP(Instruction Pointer):指令指针寄存器

CS和IP是8086CPU中最关键的寄存器,他们指示了CPU当前要读取指令的地址。

 

8086PC工作过程的简要描述

  1. 从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器
  2. IP=IP+所读取指令的长度,从而指向下一条指令
  3. 执行指令
  4. 转到步骤1,重复这个过程

 

在8086CPU加电启动或复位后(即CPU刚刚开始工作时)CS和IP被设置为CS=FFFFH,IP=0000H。

即在8086PC机刚启动时,CPU从内存FFFF0H单元中读取指令执行。

FFFF0H单元中的指令是8086PC机开机后执行的第一条指令。

这里给大家推一篇文章,将计算机的启动,写的非常棒!

 

由此,我们应该牢记一个原则,CPU将CS、IP的内容当做指令的段地址和偏移地址,用他们合成指令的物理地址,到内存中读取指令码,执行;如果说,内存中的一段信息曾被CPU执行过的话,那么,它所在的内存单元必然被CS:IP指向过。

 

修改CS、IP的指令——JMP指令

修改CS、IP

在CPU中,程序员能够使用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。

CPU从何处执行指令是由CS、IP中的内容决定的,程序员可以通过改变CS、IP中的内容来控制CPU执行目标指令。

修改CS、IP不可以使用MOV指令,8086CPU没有提供这样的功能。。

8086CPU提供了一个专门的指令来改变他们的值——JMP,例如,我们来同时修改CS、IP中的内容:

jmp指令  段地址  偏移地址

jmp 2AE3:3
jmp 3:0B16

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

仅修改IP

另外,如果我们仅仅要修改IP的内容:

jmp指令  合法寄存器

jmp  ax      //类似于mov  IP,ax
jmp  bx

功能:用寄存器中的值修改IP

 

代码段

对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。

例,可以将长度为N(N<=64KB,偏移地址长度16位)的一组代码,存在一组地址连续、起始地址为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这段内存单元是用来存放代码的,是一个代码段,它的段地址为123BH,长度是10字节。

将一段内存当作代码段,仅仅是我们在编程时的一种安排,CPU并不会由于这种安排,就自动地将我们定义的代码段中的指令当作指令来执行,还是老规矩,CPU只认被CS:IP指向的内存单元中的内容为指令(所以要将CS:IP指向所定义的代码段中的第一条指令的首地址)。

 


实验:查看CPU和内存,用机器指令和汇编指令编程

安装DEBUG

这边我们需要安装一个非常经典的学习汇编的工具,叫Debug.exe,下面是我的分享:

链接:https://pan.baidu.com/s/1yQvmjRyLsdgoTQk9UmBxvg
提取码:ld7e

这里是分享的一个debug和一个DOSBox,这是因为在我的电脑(64位的win10)中,不能直接使用debug,所以需要一个虚拟环境。

DOSBox是一种模拟器软件,主要是在IBM PC兼容机下,模拟旧时的操作系统:MS-DOS,支持许多IBM PC兼容的显卡和声卡,为本地的DOS程序提供执行环境,使这些程序可以正常运行于大多数现代计算机上的不同操作系统。DOSBox特别是为运行早期的计算机游戏所设计,主要以C++编写,是以GNU通用公共许可证许可发布的自由软件。

DOSBox可以运行那些在现代计算机上不能运行的MS-DOS软件,这些软件通常与现在的主流硬件和操作系统有一些不兼容。DOSBox在模拟MS-DOS同时,还增加了一些可用特性,包括虚拟磁盘、点对点网络、对模拟画面截图和录像。有些非官方的DOSBox变体,如DOSBox SVN Daum和DOSBox SVN-lfn提供了更多的功能,比如存档、长文件名支持等[4]。有些游戏开发商重新发行早期的DOS游戏时,也会使用DOSBox,使其可以在现代计算机上运行。

搭建细节请参考这篇文章

简单地说一下:

  1. 安装DOSBox
  2. 将debug.exe文件复制到磁盘(C:盘)的根目录,如保存在C:\盘。
  3. mount c c:\
    c:
    debug 

然后就可以进入debug了!

DEBUG常用指令

  • R:查看、改变CPU寄存器的内容
  • D:查看内存中的内容
  • E:改写内存中的内容
  • U:将内存中的机器指令翻译成汇编指令
  • T:执行一条机器指令
  • A:以汇编指令的格式在内存中写入一条机器指令

例如:

r             //查看各个寄存器
AX=0000 BX=0000 ……
r ip       //修改ip指令指针的值
:1111     //则IP就改成了1111

这里是一个DOS虚拟环境,随便改不会对电脑有害的

查看内存

使用D命令即可查看内存:

 

实战操作

写好指令

请你跟着我一步一步来完成下面的这些指令并和我一起分析,这踏实的实线将是学习汇编路上宝贵的财富。

下面我们用A命令来输入汇编指令:

-a
073F:0100 mov ax,4e20
073F:0103 add ax,1416 
073F:0106 mov bx,2000
073F:0109 add ax,bx
073F:010B mov bx,ax
073F:010D add ax,bx
073F:010F mov ax,001A
073F:0112 mov bx,0026
073F:0115 add al,bl
073F:0117 add ah,bl
073F:0119 add bh,al
073F:011B mov ah,0
073F:011D add al,bl
073F:011F add al,9C 
073F:0121
-

要注意段地址和偏移地址要结合你的电脑的情况,别一味地复制过去。

指令中的单位H是不用写的

这样我们就将汇编指令塞进了这一片内存中了。

我们可以通过

-D 073F:0100

来查看刚刚修改的内存的内容

如果不想看十六进制,还可以使用如下命令来直接看汇编指令:

-U 073F:0100

CPU指向

指令已经填写到内存中了,下面就是CPU的Show time了。

我们先用r来看看CS:IP指向哪?

现在的CS:IP指向了1111:0100,这当然不是我们的命令所在的地方,下面我们来改变CS:IP:

-r CS
:073F
-r IP
:0100

执行并观察结果

下面我们用T命令来执行机器指令并观察结果。

-t

然后就可以看到下面的寄存器内容发生改变,AX的值变成了4E20,IP指令指针也自动变成0103。

后面的那一大溜指令相信聪明的你知道该怎么去做了,那这篇博客就写到这里了。

 

 


补充实验

题目

下面是一个简单的补充实验:

PC机主板上的ROM(只读存储器)上写有一个生产日期,在内存FFF00H~FFFFFH的某几个单元中,请找出这个生产日期并试图改变它。

实践

首先用d命令来查看,其中第二个参数表示查看的范围,这样就可以直接显示出来FFF00H~FFFFFH。

-d fff0:0 ff

我们可以看到下面显示出来了我们寻找的日期,成功了。

这里的字符是以ASCII码来显示的,故00110000即为0,00110001即为31,如此类推

下面我们用E来尝试修改内存数据。

-E ffff:05
FFFF:0005  31.32

企图将31改成32。

然后使用

-d fff0:0 ff

然后我们会发现,数据没有变化

其实没有变化是对的,因为这个区域的数据是刻录在主板BIOS中,即只读存储器中,在一般情况下这里是改写不了的,只读。

 


微机原理中的寄存器总汇

这学期正好有“微机原理”这门课,正好学到这部分内容,我就做了一个小思维导图,是8086机中的寄存器汇总:

 

 

 

 

 

 

商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

是的,我就是计算机界的枭雄!