汇编语言学习笔记(一)

0x00 基础知识

  • Central Processing Unit为中央处理单元,简称CPU。CPU是一种微处理器,计算机是指由CPU和其他受CPU直接或间接控制的芯片、器件、设备组成的计算机系统,比如常见的PC机。每一种CPU都有自己的汇编指令集。
  • 汇编指令通过编译器翻译为机器码,供计算机直接使用。
  • 汇编语言由汇编指令、伪指令和其他符号组成。
  • 指令和数据在存储器中存放,也就是平时所说的内存。在内存或磁盘上,指令和数据没有任何区别,都是二进制信息
  • 存储器被划分成若干个存储单元,每个存储单元从0开始顺序编号,微机存储器的容量以字节为最小单位计算。
  • CPU通过地址总线指定存储器单元,地址总线的宽度决定了CPU的寻址能力。
  • CPU通过数据总线实现自身与内存或其他器件之间的数据传送,数据总线的宽度决定了CPU与其他器件进行数据传送时的一次数据传送量。8088CPU数据总线宽度为8,8086CPU数据总线宽度为16。
  • CPU通过控制总线实现对外部器件的控制,控制总线的宽度决定了CPU对系统中其他器件的控制能力。
  • 每台PC机都拥有一个主板,主板上有核心器件和一些主要器件,包括CPU、存储器、外围芯片组、扩展插槽等,这些器件通过总线相连。
  • 存储器从功能和连接上可分为三类:随机存储器RAM、装有BIOS(Basic Input/Output System,基本输入输出系统)的只读存储器ROM、接口卡上的RAM(如显存)。
  • PC机在实际运作过程中,所有的物理存储器被看作一个由若干存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间。

0x01 寄存器

一个典型的CPU由运算器、控制器、寄存器等器件构成。各器件功能如下:

  • 运算器进行信息处理。
  • 寄存器进行信息存储。
  • 控制器控制各种器件进行工作。
  • 内部总线连接各种器件,在它们之间进行数据的传送。

通用寄存器

8086CPU的所有寄存器都是16位的,可存放两个字节。AX、BX、CX、DX这四个寄存器通常用来存放一般性数据,称为通用寄存器。同时,这四个寄存器可分为两个可独立使用的8位寄存器来使用。

以寄存器AX为例,AX的低8位构成AL寄存器,高8位构成AH寄存器。AH和AL寄存器是可以独立使用的8位寄存器。注意,诸如mov ax,bl这类的汇编语句是错误的,因为其尝试将一个8位寄存器中的值赋值至16位寄存器中。

8086CPU可以一次性处理以下两种尺寸的数据:

  • 字节:记为byte,一个字节由8个bit组成,可存在8位寄存器中。
  • 字:记为word,一个字由两个字节组成,这两个字节分别称为这个字的高位字节(H)和低位字节(L)。

汇编指令

  • 在写一条汇编指令或一个寄存器的名称时,不区分大小写。

  • 8位寄存器只能存放两位十六进制的数字,若溢出则丢弃最高位。

    1
    2
    mov al, C5H
    add al, 93H

    以上代码执行结果为:158H,而寄存器al是8位寄存器,故舍弃最高位1,保存结果58H

物理地址

所有的内存单元构成的存储空间是一个一维的线性空间,每个内存单元在这个空间中都有唯一的地址,将该地址称为物理地址。在CPU向地址总线上发出物理地址之前,必须要在内部先形成这个物理地址。

8086CPU是16位结构的CPU,具有以下特性:

  • 运算器一次最多可以处理16位的数据。
  • 寄存器的最大宽度为16位。
  • 寄存器和运算器之间的通路为16位。

8086CPU有20位地址总线,可以传送20位地址。然而,该CPU又是16位结构,在内部一次性处理、传输、暂时存储的地址均为16位。故8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位物理地址

具体来讲,地址加法器采用物理地址=段地址*16+偏移地址的方法使用段地址和偏移地址合成物理地址。该方法的本质含义是:CPU在访问内存时,用一个基础地址(段地址16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。一般来说,这种寻址功能是”*基础地址+偏移地址=物理地址“寻址模式的一种具体实现方案。

以上寻址方式有以下几个关键点:

  • CPU可以用不同的段地址和偏移地址形成同一个物理地址。
  • 偏移地址16位,变化范围为0~FFFFH,仅用偏移地址来寻址最多可寻64KB个内存单元。

段寄存器

可以根据需要,将地址连续、起始地址为16的倍数的一组内存单元定义为一个段。8086CPU有4个段寄存器:CS、DS、SS、ES。当8086CPU要访问内存时由这4个段寄存器提供内存单元的段地址。

CS和IP是8086CPU中两个最关键的寄存器,指示了CPU当前要读取指令的地址。CS为代码段寄存器,IP为指令指针寄存器。任意时刻,CPU将CS:IP指向的内容当作指令执行。

8086CPU工作过程如下:

  • CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
  • IP = IP + 所读取指令的长度,指向下一条指令。
  • 执行指令,转到第一步并重复。

能够改变CS、IP内容的指令被统称为转移指令,最简单的转移指令是jmp指令。可以使用形如jmp 段地址:偏移地址的指令实现对CS、IP内容的同时修改。若只想修改IP的内容,可以使用形如jmp 某一合法寄存器的指令完成,该指令表示使用寄存器中的值修改IP。

0x02 Debug的使用及相关指令

Windows10系统中已经剔除Debug相关的插件,需要下载DOSBox并手动安装debug.exe才可以使用Debug程序。安装指南链接如下:win10环境下如何安装和运行DOSBox和debug_mengjizhiyou的博客-CSDN博客

Debug程序中重要指令如下:

  • R命令

    查看、修改CPU中寄存器的内容。r命令可以直接查看所有寄存器的值,使用r+寄存器名称语句以修改寄存器的值。

    1

  • D命令

    查看内存中的内容。

    直接使用d命令将列出Debug预设的地址处的内容。使用命令d 段地址:偏移地址的格式来指定查看内存的起始地址,随后接着使用d命令可列出后续内容。采用d 段地址:起始偏移地址 结尾偏移地址来规定d命令的查看范围。

    Debug将输出的内容分为三部分:中间是从指定地址开始的128个内存单元的内容,以十六进制的格式输出,每行的输出从16的整数倍地址开始,最多输出16个单元的内容。左边是每行的起始地址。右边是每个内存单元中的数据对应的可显示的ASCII码字符。

    2

    同时,D命令支持d 段寄存器:偏移地址指令格式。

    6

  • E命令

    修改内存中的内容,可以写入数据和指令,在内存中它们实际上没有区别。

    可以使用e 起始地址 数据 数据 数据 ...的格式修改指定内存范围中的内容。也可以直接输入e 起始地址,随后以询问的方式逐个修改内存单元中的内容,空格键表示指定内存单元修改完成,Enter键表示e命令执行结束。

    3

  • U命令

    将内存中的内容解释为机器指令和对应的汇编指令。

    4

  • T命令

    执行CS:IP指向的内存单元处的指令。若需要执行指定内存处存放的指令,则需要利用r命令修改寄存器CS和IP的值,随后才可使用t命令完成指令执行。

    值得注意的是,T命令在修改寄存器SS的指令时,下一条指令也紧接着被执行,这涉及中断机制。

    7

  • A命令

    以汇编指令的形式向内存中写入指令。

    5

0x03 寄存器内存访问

字的存储

在内存中存储时,由于内存单元是字节单元,则一个字需要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放在高地址单元中。我们将起始地址为N的字单元简称为N地址字单元。

任何两个地址连续的内存单元既可被看作两个内存单元,也可 被看作一个地址为N的字单元中的高位字节单元和低位字节单元。

DS&[address]

8086CPU中有一个DS寄存器,通常用来存放要访问数据的段地址。

可以利用mov指令将一个内存单元中的内容送入一个寄存器中。寄存器通过寄存器名标识,而内存单元需要用内存单元地址指明。[address]表示一个内存单元,address表示内存单元的偏移地址。指令执行时,CPU自动区ds寄存器中的数字作为内存单元的段地址。

值得注意的是,8086CPU不支持将数据直接送入段寄存器的操作,故需要使用一般寄存器完成中转。

1
2
3
4
mov ds,1000H ;错误语句

mov ax,1000H
mov ds,ax ;正确语句

mov&add&sub

以上三个指令均支持以下操作:

1
2
3
4
* 寄存器,数据
* 寄存器,寄存器
* 寄存器,内存单元
* 内存单元,寄存器

其中*代表mov,add和sub三类运算。

0x04 栈

基本概念及特性

栈是一种具有特殊访问方式的存储空间,具有后进先出(Last In First Out, LIFO)的特性。

基本操作

8086CPU提供入栈和出栈指令,分别为PUSH和POP,前者将一个新的元素放到栈顶,后者从栈顶去除一个元素。8086CPU的入栈出栈操作都是以字为单位进行的。

入栈时,栈顶从高地址向低地址方向增长,出栈则相反。栈顶元素出栈后,内存对应单元的元素依然存在,但已不在栈中,当再次执行PUSH指令时,写入的新数据会将其覆盖。

SS&SP

8086CPU中存在段寄存器SS和栈指针 寄存器SP。栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。在对栈进行操作前,首先需要初始化寄存器SS和SP。

例如,若对范围为22000H~2200FH范围的栈进行操作,需首先初始化 SS指向2200H,初始化SP指向000FH+1=0100H。

PUSH&POP

PUSH指令执行过程分为两步:首先执行SP = SP-2,使SP指向新的栈顶元素;随后向SS:SP指向的字单元中送入数据。POP指令执行过程同样分为两步:首先从SS:SP指向的字单元中读取数据;随后执行SP = SP-2移动SP指针。

可以看出,PUSH和POP等栈操作指令修改的只是SP寄存器,也就是说栈顶的变化范围最大为0~FFFFH。

当栈满的时候再次使用PUSH指令入栈,或栈空的时候再次使用POP指令出栈,都将发生栈顶越界问题

0x05 段的综述

我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。根据使用者的初始化情况,一段内存可以作为代码的存储空间、数据的存储空间和栈空间,并支持这三类空间类型的组合。

  • 数据段

    数据段的地址放在DS中,使用mov、add、sub等访问内存单元的指令时,CPU就将指定内存单元中的内容作为数据来处理。

  • 代码段

    代码段的段地址放在CS中,段中第一条指令的偏移地址放在IP中,CPU将执行指定内存单元中的指令。

  • 栈段

    栈段的段地址 放在SS中,栈顶单元的偏移地址放在SP中,CPU在执行PUSH、POP等栈操作时就将指定内存单元作为栈空间使用。

请作者吃个小鱼饼干吧