汇编语言学习笔记

一、基础知识

1. 汇编语言简介

从机器语言到汇编语言:

机器语言:由“0”“1”数字编成,以二进制为基础表示电子器件的高低电平,从而执行机器指令,进行运算。

明显弊端:难以书写和阅读,复杂难懂。

于是,程序员写出了编译器来翻译机器语言,汇编语言诞生了。

汇编语言:主体为汇编指令。如“mov ax,bx”。操作对象为CPU、寄存器和内存。

2. CPU基本工作原理

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

2.1 存储单元、指令和数据

指令和数据:

指令和数据是应用上的概念。在内存或磁盘上,指令和数据没有任何区别,都是二进制信息。CPU 在工作的时候把有的信息看作指令,有的信息看作数据,为同样的信息赋予了不同的意义。()

存储单元:

存储器被划分成若干个存储单元,每个存储单元从0开始顺序编号。一个存储器能存储128个Byte。

内存计量单位:
8bit=1B 1KB=1024B 1MB=1024KB 1GB=1024MB 1TB=1024GB

2.2 CPU对存储器的读写

存储器被划分成多个存储单元,存储单元从零开始顺序编号。这些编号可以看作存储单元在存储器中的地址
CPU 要从内存中读数据,首先要指定存储单元的地址
CPU 在读写数据时还要指明,它要对哪一个器件进行操作,进行哪种操作,是从中读出数据,还是向里面写入数据。

可见,CPU想进行数据的读写需要和外部器件进行以下三类信息交互:

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

2.3 三大总线

CPU通过导线将电信号传到存储器芯片中,从而实现信息的交互。

在计算机中专门有连接 CPU 和其他芯片的导线,通常称为总线。总线从物理上来讲,就是一根根导线的集合。 根据传送信息的不同,总线从逻辑上又分为3类,地址总线、控制总线和数据总线
示例:
传输图例
(1) CPU通过地址线将地址信息了发出。
(2) CPU通过控制线发出内存读命令,选中存储器芯片,并通知它,将要从中读取数据。
(3) 存储器将3号单元中的数据8通过数据线送入CPU。
写操作与读操作的步骤相似。如向3号单元写入数据26:
(1) CPU通过地址线将地址信息了发出。
(2) CPU通过控制线发出内存写命令,选中存储器芯片,并通知它,要向其中写入数据。
(3) CPU通过数据线將数据26送入内存的3号单元中。

要让一个计算机或微处理器工作,应向它输入能够驱动它进行工作的电平信息(机器码)。CPU 按收这条机器码后将完成我们上面所述的读写工作。 机器码难于记忆,用汇编指令来表示,情况如下。
机器码: 10100001 00000011 00000000
对应的汇编指令:MOV AX,[3]
含义: 传送3号单元的内容入AX

由此可见,汇编指令可以方便地直接对CPU进行操作。

地址总线: CPU是通过地址总线来指定存储器单元的,地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。
一个CPU有N根地址线,则可以说这个CPU的地址总线的宽度为N。这样的CPU可以寻找2ⁿ个内存单元。

数据总线: CPU与内存或其他器件之间的数据传送是通过数据总线来进行的。数据总线的宽度决定了CPU和外界的数据传送速度。
8根数据总线一次可传送一个8位二进制数据(即一个字节)。16根数据总线一次可传送两个字节。

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

  • 地址总线的宽度决定了CPU 的寻址能力;
  • 数据总线的宽度决定了CPU 与其他器件进行数据传送时的一次数据传送量:
  • 控制总线的宽度决定了CPU 对系统中其他器件的控制能力。

3. 内存地址空间

CPU在操控存储器的时候,把它们都当作内存来对待,把它们总的看作一个由若干存储单元组成的逻辑存储器,这个逻辑存储器就是我们所说的内存地址空间。

逻辑存储器
在图1. 8中,所有的物理存储器被看作一个由若干存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间。CPU在这段地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据。

内存地址空间的大小受CPU地址总线宽度的限制。8086CPU的地址总线宽度为20,可以传送2²⁰个不同的地址信息(大小从0至2²⁰−1)。即可定位2²⁰个内存单元,则8086PC的内存地址空间大小为1MB。同理,80386CPU的地址总线宽度为32,则内存地址空间最大为4GB。

二、寄存器与简单汇编指令

1.* 简单汇编指令

1.1 mov,add和sub指令

mov指令(传输指令)
用C语言可理解为赋值语句,如"mov ax,bx"
可理解成"ax=bx"。

使用形式如下:
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指令
类似C语言中"+=“和”-="这两个运算符号,如"add ax,8"和"sub ax,8"可分别翻译成"ax+=8"和"ax-=8"这两种运算。

使用形式可参照上述方式用debug自行实验,不再赘述。

1.2 jmp指令(转移指令)

用于修改CS和IP的值来控制CPU执行目标指令。
若想同时修改CS、IP的值,可用
jmp 段地址:偏移地址”的指令完成。如:
jmp 2AE3:3,执行后,CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令。
jmp 3:0B16,执行后,CS=0003H,IP=0B16H,CPU将从00B46H处读取指令。
若只想修改IP的值,可用“jmp 某一合法寄存器”完成。如:
jmp ax,在含义上好似“mov IP,ax”意思是用寄存器的值修改IP。

1.3 push和pop指令(入栈、出栈指令)

push和pop指令是可以在寄存器和内存(栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。)之间传送数据的。

push和pop指令的格式可以是如下形式:

push 寄存器;将一个奇存器中的数据入栈
pop 寄存器;出栈,用一个寄存器接收出栈的数据

(PS:用什么类型数据入栈,就用什么类型数据出栈。)

指令执行时,CPU要知道内存单元的地址,可以在push、pop 指令中只给出内存单元的偏移地址,段地址在指令执行时,CPU 从ds 中取得。

  • 用栈来暂存以后需要恢复的寄存器中的内容时,出栈的顺序要和入栈的顺序相反,因为最后入栈的寄存器的内容在栈顶,所以在恢复时,要最先出栈。
  • 将寄存器清零,可以用

mov ax,0
sub ax,ax
xor ax,ax

2. 寄存器

一个典型的CPU由运算器、控制器、寄存器(CPU工作原理)等器件构成,这些器件靠内部总线相连。其中寄存器进行信息存储。程序员正是通过改变各种寄存器中的内容来实现对CPU的控制。

每一代CPU都是向下兼容的。

8086CPU 的上一代CPU中的寄存器都是8位的,为了保证兼容,使原来基于上代CPU编写的程序稍加修改就可以运行在8086之上,8086CPU的AX、BX、CX、DX这4个寄存器都可分为两个可独立使用的8位寄存器来用

  • AX可分为AH和AL;
  • BX 可分为BH 和BL;
  • CX 可分为CH 和CL;
  • DX可分为DH和DL。

以 AX 为例,8086CPU的16位寄存器分为两个8位寄存器的情况如图2. 3所示。
图23

2.1 字在寄存器中的存储

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

  • 字节: 记为byte,一个字节由8个bit组成,可以存在8位寄存器中。
  • 宇:记为word,一个字由两个字节组成,这两个字节分别称为这个字的高位字节和低位字节,一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中。一个字型数据20000,存在AX寄存器中,在AH中存储它的高8位,在AL中存储了它的低8位。AH和AL中的数据,既可以看成是一个字型数据的高8位和低8位,这个字型数据的大小是20000:又可以看成是两个独立的字节型数据,它们的大小分别是78和32。
  • 高位存储高地址,低位存储低地址。
  • 一个字要用两个地址连续的内存单元来存放
  • 任何两个地址连续的内存单元,N号单元和 N+1号单元,可以将它们看成两个内存单元,也可看成一个地址为N的字单元中的高位字节单元和低位字节单元。

字节型数据:该地址存放数据,是一个字节。
字型数据:整体所指数据,是一个字。

2.2 物理地址与偏移地址

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

CPU 通过地址总线送入存储器的,必须是一个内存单元的物理地址。在 CPU 向地址总线上发出物理地址之前,必须要在内部先形成这个物理地址。不同的CPU可以有不同的形成物理地址的方式。

16位CPU(8086CPU):

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

8086CPU有20位地址总线,可以传送20位地址,达到IMB寻址能力。8086CPU又是16位结构,在内部一次性处理、传输、暂时存储的地址为16位。从8086CPU的内部结构来看,如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出的寻址能力只有64KB。8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。
图2.6
(1)CPU中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址;
(2)段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
(3)地址加法器将两个16位地址合成为一个20位的物理地址;
(4)地址加法器通过内部总线将20位物理地址送入输入输出控制电路;
(5)输入输出控制电路将20位物理地址送上地址总线;
(6)20位物理地址被地址总线传送到存储器。

地址加法器采用**“物理地址=段地址x16+偏移地址”**的方法用段地址和偏移地址合成物理地址。

“段地址x16”的含义:
(1)一个数据的二进制形式左移1位,相当于该数据乘以2:
(2)一个数据的二进制形式左移N位,相当于该数据乘以2的N次方;
(3)地址加法器完成段地址×16的运算就是将以二进制形式存放的段地址左移4位。
(4)进一步可知一个X进制的数据左移1位,相当于乘以X。

“物理地址=段地址x16+偏移地址”的本质含义
CPU在访问内存时,用一个基础地址(段地址×16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。
更一般地说,8086CPU的这种寻址功能是 “基础地址+偏移地址=物理地址”寻址模式的一种具体实现方案。8086CPU中,段地址×16可看作是基础地址。

2.3 段

(1) 基本概念

其实,内存并没有分段,段的划分来自于CPU我们可以用分段的方式来管理内存。(人为规定的)
将若干地址连续的内存单元看作一个段,用段地址×16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。
段地址x16必然是16的倍数,所以一个段的起始地址也一定是16的倍数:偏移地址为16位,16位地址的寻址能力为64KB,所以给定一个段地址,仅通过变化偏移地址来进行寻址,偏移地址16位,变化范围为0- FFFFH,仅用偏移地址来寻址最多可寻64KB个内存单元。

PS:

  • CPU 可以用不同的段地址和偏移地址形成同一个物理地址
  • “数据在21F60H内存单元中。”有两种类似的说法:
    1.数据存在内存2000:1F60单元中;
    2.数据存在内存的2000H段中的1F60H单元中。
(2) 段寄存器

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

(3) CS和IP

CS和IP是8086CPU中两个最关键的寄存器,它们指示了CPU当前要读取指令的地址。CS 为代码段寄存器,IP 为指令指针寄存器,从名称上我们可以看出它们和指令的关系。
在8086PC机中,任意时刻,设CS中的内容为M,IP 中的内容为N,8086CPU将从内存Mx16+N 单元开始,读取一条指令并执行。
也可以这样表述:8086机中,任意时刻,CPU将CS:IP 指向的内容当作指令执行。
图2.10
如图可知,将数据存放到寄存器中需要三个字节,对两个寄存器进行操作需要两个字节。

8086CPU的工作过程可简要描述如下:
(1)从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP=IP+所读取指令的长度,从而指向下一条指令;
(3)执行指令。转到步骤(1),重复这个过程。

CPU将CS: IP指向的内存单元中的内容看作指令,因为,在任何时候,CPU将CS、IP 中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。

(4)代码段

可以根据需要,将一组内存单元定义为一个段。我们可以将长度为N(N≤64KB)的一组代码,存在一组地址连续、起始地址为16的倍数的内存单元中,我们可以认为,这段内存是用来存放代码的,从而定义了 一个代码段。(同样是人为规定的,CPU不知道,它只负责读写指令,执行)

(5)数据段

我们可以将一组长度为 N( ≤ 64KB) 、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段用DS存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。(同上)

(6) DS和[address]

内存地址由段地址和偏移地址组成。8086CPU 中有DS寄存器,通常用来存放要访问数据的段地址。

“[…]”表示一个内存单元,“[…]”中的0表示内存单元的偏移地址。
"mov al,[0]"指令执行时,8086CPU自动取DS中的数据为内存单元的段地址。
10000H 用段地址和偏移地址表示为1000:0,我们先将段地址1000H放入ds,然后用"mov al,[0]"完成传送。mov指令中的[0]说明操作对象是一个内存单元,[0]中的"0"说明这个内存单元的偏移地址是0,它的段地址默认放在DS中,指令执行时,8086CPU会自动从DS中取出。
“数据——>通用寄存器——>段寄存器”

mov bx, 1000H
mov ds,bx

是给段寄存器赋值的常用操作。

下面是字传送的详细操作:
在这里插入图片描述
在这里插入图片描述

三、*栈

1. 栈的概念

栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。

2. CPU栈机制

入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出一个元素。栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。栈的这种操作规则被称为:LIFO(Last In First Out,后进先出)。
从程序化的⻆度来讲,应该有一个标记,这个标记一直指示着栈顶。(SS:SP)

  • 栈从栈底(高位)开始存入数据,从栈顶(低位)开始复制数据转移出寄存器。

3. SS和SP

CPU 如何知道栈顶的位置?显然,也应该有相应的寄存器来存放栈顶的地址,8086CPU中,有两个寄存器,段寄存器SS和寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。push指令和pop指令执行时,CPU 从SS 和SP 中得到栈顶的地址。

push ax 的执行,由以下两步完成。
(1)SP=SP-2,SS:SP 指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶;
(2)将ax中的内容送入SS:SP 指向的内存单元处,SS:SP此时指向新栈顶。

pop ax的执行过程和push ax刚好相反,由以下两步完成。
(1)将SS:SP 指向的内存单元处的数据送入ax 中;
(2)SP=SP+2,SS:SP 指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

  • push指令先指定空位置,再存入数据
  • pop指令先存入数据到寄存器中,再改变SS:SP位置
  • pop 操作前的栈顶元素依然存在,但是,它己不在栈中。当再次执行 push 等入栈指令后,SS:SP移至上一个内存单元,并在里面写入新的数据,它将被覆盖。
    故磁盘格式化并没有清除数据,只是把“指针”复位了而已,下次写入只是覆盖原有数据,可以复原。

4. 栈溢出

将 10010H~1001FH当作栈空间,该栈空间容量为16字节(8字),初始状态为空,SS=1000H、SP=0020H,SS:SP 指向10020H;
在执行8次push ax 后,向栈中压入8个字,栈满,SS: SP 指向 10010H;
再次执行 push ax: sp=sp- 2,SS:SP 指向 1000EH,栈顶超出了栈空间,ax 中的数据送入1000 EH 单元处,将栈空间外的数据覆盖
上面描述了执行push、pop 指令时,发生的栈顶超界问题。可以看到,当栈满的时候再使用push 指令入栈,或栈空的时候再使用pop 指令出栈,都将发生栈顶超界问题(栈溢出)。

栈顶超界是危险的,因为我们既然将一段空间安排为栈,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等,这些数据、代码可能是我们自己程序中的,也可能是别的程序中的(毕竟一个计算机系统中并不是只有我们自己的程序在运行)。但是由于我们在入栈出栈时的不小心,而将这些数据、代码意外地改写,将会引发一连串的错误。

PS: 如果是故意造成栈溢出的,就可以利用此特性将一些程序中的受保护数据取出(例如用户登录密码等信息),所以栈溢出也是PWN攻击的一种方式。

8086CPU只知道栈项在何处(由SS:SP指示) ,而不知道我们安排的栈空间有多大。这点就好像CPU只知道当前要执行的指令在何处(由CS:IP指示),而不知道要执行的指令有多少。从这两点上我们可以看出8086CPU 的工作机理,它只考虑当前的情况:当前的栈顶在何处、当前要执行的指令是哪一条。

5. 栈段

我们可以将长度为N( ≤ 64KB) 的一组地址连续、起始地址为16的倍数的内存单元,当作栈空间来用,从而定义了一个栈段。比如,我们将 10010H~1001FH 这段长度为16字节的内存空间当作栈来用,以栈的方式进行访问。这段空间就可以称为一个栈段。

如果将 10000H~1FFFFH 这段空间当作栈段,SS=1000H,栈空间为64KB,栈最底部的字单元地址为1000:FFFE。任意时刻,SS:SP 指向栈顶单元,当栈中只有一个元素的时候,S S=1000H,SP=FFFEH。栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2.
SP原 来为FFFEH,加2后SP=0,所以,当栈为空的时候,SS=1000H,SP=0。

换一个⻆度看,任意时刻,SS:SP 指向栈顶元素,当栈为空的时候,栈中没有元素,也就不存在栈顶元素,所以sS:SP 只能指向栈的最底部单元下面的单元,该单元的地址为栈最底部的字单元的地址+2。栈最底部字单元的地址为1000:FFFE ,所以栈空时,SP=0000H.

段的总结

我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。这完全是我们自己的安排。
不管我们如何安排,CPU将内存中的某段内容当作代码,是因CSIP 指向了那里:CPU将某段内存当作栈,是因为SS;SP 指向了那里。我们一定要清楚,什么是我们的安排,以及如何让CPU 按我们的安排行事。要非常清楚 CPU 的工作机理,才能在控制 CPU 按照我们的安排运行的时候做到游刃有余。

四、使用Debug进行简单汇编指令编程

1.DOSBox和Debug安装及使用

由于目前的Windows系统不再支持Debug的直接使用,所以我们需要先安装DOSBox和Debug来进行汇编指令编程。

安装

安装地址网上一般都能搜到,这里只贴出:DOSBox的地址:https://www.dosbox.com

使用

点开安装后一直Next,选个你需要的磁盘位置安装(一般是D盘)就可以,安装Debug的时候需要注意Debug的文件路径。

  1. 打开DOSBox后,使用“mount c Debug位置”来让DOSBox进入Debug程序运行;然后输入“C:”
    ;最后输入“debug”就能运行了。
    但是每次进入DOSBox程序时都要重复输入该指令,所以我们可以打开DOSBox文件位置,找到“…Option.bat”文件,然后如图把指令挪过来:
    DOSBox配置
    以后每次打开就能直接运行Debug程序了。
  2. 打开DOSCBox后可以发现窗口无法放大缩小,可以打开“…Option.bat”文件后找到
    “windowresolution=original
    output=surface”代码,将它改为
    “windowresolution=你需要的分辨率(如1200x800)
    output=opengl”就能正常放大程序窗口了。

2.Debug命令

可以参照这篇文章进行了解和学习:
一般是先用r命令查看当前寄存器内部的值,再用a命令进行编程。
汇编语言DEBUG命令详解||汇编命令||DEBUG的常用命令:A,U,R,T,D,E,Q 等等

五、程序的编写与执行

汇编语言程序写出到执行的过程: 写出源程序—>对源程序进行编译连接—>生成可执行文件—>操作系统将其加载入内存—>CPU执行程序

1.源程序

源程序包括汇编语言和伪指令。
示例:
在这里插入图片描述

伪指令

伪指令是给编译器看的指令,编译器根据伪指令来进行相关编译工作。

(1) segment与ends

作用:定义一个段。
segment 表示一个段的开始,ends 表示一个段的结束。

用法:

段名 segment

段名 ends

(2) end

作用:结束编译。
PS: 注意区分end与ends的含义。

用法:在程序结尾处使用。

(3) assume

作用:将段与指定的段寄存器关联起来。(即声明该段在CPU中的执行位置)

用法:在一个段的开头使用。

assume 段寄存器:段名

(4) 标号

一个标号指代了一个段地址。
它被编译后最终处理为一个段地址。

程序与程序结构

图4.2

程序返回

(1)DOS中的程序运行

DOS是一个单任务的操作系统。

当要运行XXX.exe程序时,DOS程序会终止运行,CPU将控制权移交给此exe程序,程序运行完成后,会返回DOS程序继续运行,等待下一条指令的输入。

我们称这个过程为程序返回

原理详见CSAPP中对于进程与线程的阐述。

(2) 实现程序返回(中断机制)

在程序末尾添加返回的程序段。

mov ax,4c00H
int 21H

PS: 这是汇编指令,由CPU负责执行。

小结:
表4.1

逻辑错误与语法错误

被编译器发现的错误是语法错误;编译后,在运行时发生的错误是逻辑错误。
一个是语句错误书写,一个是缺少必要语句或者本身的逻辑问题

2.环境配置

(1)下载Notepad++(用于编写源程序,用其他文本软件也可以)
(2)下载MASM,并将这几个程序与源程序文件放在一起
(3)在Notepad++上编写源程序并保存到上述的文件夹中

3.源程序的编译、连接与执行

(1)如源程序的示例那样编写好源程序后,我们就可以将其保存为一个XXX.asm的文件,然后进入DOSBox程序,输入以下命令以打开源程序文件所在位置:

mount c D:\StudyAssembler\MASM\ (文件所在位置)
C:

如图所示:
step1:

(2)输入以下命令以编译源程序:

masm 1.asm

如下图所示:
在这里插入图片描述

(3)编译完成后会生成一个1.obj对象文件,输入以下命令以连接程序:

link 1.obj

由于书本中的1.asm文件只有一个代码段,所以无需作其他配置,接下来一直回车直到出现命令行的出现。

如下图所示:
在这里插入图片描述
(4)输入“1.exe”运行程序,会看到一个短暂的黑屏,然后回到DOS命令行,程序运行完毕。

至此,我们完成了1.asm文件的编译、连接和运行,但是,我们并没有看到运行程序的过程。

PS:

  • XXX.com文件大小为64KB,只能有一个段。
    XXX.exe文件可以很大,其中一般有多个段。
    所以当你编写的程序中只有一个段,可以考虑使用XXX.com文件执行,可以用于伪装。
  • 可以用“ML 1.asm”指令来编译并连接程序,并且文件后缀和文件名有时可省略,因为已默认文件位置和类型。
  • 可以指定文件生成的位置,只需在执行文件前加上路径
  • Link的作用:可以将源程序分开编译,然后将它们连在一起
  • 在命令行输入指令后加分号,可以使编译器忽略中间文件的生成,更简捷

4.对于DOS程序运行原理的解释

(1)任何通用系统都要提供shell(外壳)程序,用户使用此程序来操作计算机系统进行工作。
(2)DOS中有一个程序command.com,这个程序在DOS中称为命令解释器,也就是DOS系统的shell。
(3)DOS启动时,先完成其他重要的初始化工作,然后运行command.com,command.com运行后,执行完其他的相关任务后,在屏幕上显示出由当前盘符和当前路径组成的提示符,比如:“c:\”或“c:\windows”等,然后等待用户的输入。
(4)command首先根据文件名找到可执行文件,然后将这个可执行文件中的程序加载入内存,设置CS:IP指向程序的入口。此后,command暂停运行,CPU运行程序。程序运行结束后,返回到command中,command再次显示由当前盘符和当前路径组成的提示符,等待用户的输入。在DOS中,command处理各种输入:命令或要执行的程序的文件名。我们就是通过command来进行工作的。

故汇编程序的完整执行过程为:
编程 (Edit) 一>l.asm一>编译(masm)一>1.obj一>连接(link)一>1.exe一>加载 (command) 一>内存中的程序一>运行(CPU)

5.跟踪程序的执行

在源程序的汇编指令前加标号(对段命名),并在end后加上这个标号,就给程序设置了一个入口。
Debug利用中断指令实现对程序的跟踪。
CX中存放程序的长度。
图4.20
上图说明内存中程序存放在DS地址+10H(即256字节)处,这256字节里存放着PSP,DOS用来和程序通信。
故程序的物理地址为:(X+16)x16+0 (左移一格)
可用段地址和偏移地址:X+10H:0

Debug的执行原理:command加载Debug,Debug加载1.exe。程序结束后从1.exe中的程序返回到Debug,从Debug返回到command。

参考文献:
《汇编语言》(第三版)王爽著

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值