一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序

文章目录

  • 进度:31/77
  • 四、第一个程序:22/77
    • 1. 源程序结构
    • 2. 程序返回
    • 3. 程序错误
    • 4. 第一个程序
    • 5. 程序执行过程
    • 6. Debug单步执行
    • 实验3
  • 五、[BX]和loop指令:28/77
    • 1. debug运行程序和可执行文件写代码的不同
    • 2. 内存单元&描述性符号
    • 3. Loop指令
      • 3.1 用循环计算10次方
      • 3.2 值得注意的点&调试
    • 4. Debug和汇编编译器masm对指令的不同处理
    • 5. 安全的内存空间
    • 6. 复制内存内容
  • 六、包含多个段的程序:31/77
    • 1. 程序的入口
    • 2. 程序中使用栈
    • 检测点6.1
    • 3. 在程序中使用不同的段
    • 实验5

基础不会,啥都白费;基础不牢,地动山摇。
汇编在基础中的地位举足轻重,学习汇编,可以帮助我们从CPU角度出发,理解程序,写出更好的 高级语言 程序。可以帮助我们理解程序的运行机制,知道原理,解决一些隐蔽的BUG。
学习步骤: 看视频,看书,做笔记,理解为主。习题独立完成,全对,才能进入下一章的学习。
视频: 小甲鱼
教材: 《汇编语言(王爽)》(三版)
日期: 2020-07-01

进度:31/77

四、第一个程序:22/77

编写
编译连接
执行
  1. 编写源程序文件
  2. 编译链接可执行文件
  3. 执行可执行文件中的程序

可执行文件:

  1. 程序(汇编指令翻译成的机器码) + 数据(源程序中定义的数据)
  2. 描述信息(如,程序有多大、要占用多少内存空间)

1. 源程序结构

源程序: 源程序文件中的所有程序,皆称为源程序
程序: 源程序中最终 由计算机执行,处理的指令或数据。


汇编源程序由伪指令汇编指令 构成


汇编指令: 用来翻译成机器码
伪指令: 给编译器执行的,让编译器执行相关编译工作。
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第1张图片

;在汇编中表示注释


  • 段名 segment段名 ends是一对,定义一个段,这个段用来存放代码。(ends后面的s表示的是segment而不是复数的意思)

标号: XXX segment里的XXX就是标号。它是一个段的名称 ,最终会被编译,连接成为一个段的段地址(就类似C中的指针)

  • end用来标识程序的结束
  • assume假设寄存器和程序中的某一个XXX segment ... XXX ends段相关联,很像给寄存器取别名

注意: 至少要有一个段(代码段)


下图展示了汇编指令可执行文件 的过程
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第2张图片

2. 程序返回

DOS是一个单任务系统:

  • 一个程序p2想要运行,必须得有一个正在运行的p1,把它从可执行文件加载入内存,并将CPU的控制权交给p2,p2才能运行。
  • p2运行时,p1暂停运行。p2运行完毕,p1继续运行。

比如仿DOS程序CMD,执行p2.exe时,CMD.exe停止执行。等到p2.exe执行完毕后,CMD.exe再次执行。

一个程序结束,然后返回调用的程序的行为,叫做程序的返回 ret


要让程序返回,需要以下两行代码
在这里插入图片描述
int 21H是中断的意思,中断机制是DOS的发明。在Win中,变成了消息机制,从而单任务系统变为多任务系统。

注意: 上面两行代码是固定的,原理后面再讲。

3. 程序错误

程序错误分为:语法错误逻辑错误

  • 语法错误: 编译器报错
  • 逻辑错误: 运行报错

很类似JAVA中的运行期异常编译期异常 。但是 错误异常 一定要区分开来。错误是不可避免的,异常是可以避免的。所以,语法错误叫成 语法异常 更准确。逻辑错误,可能是由于代码不规范,那就应该列为 逻辑异常 ;如果是由于溢出之类导致内存出错之类的,应该列为 逻辑错误


源文件得不到目标文件的两类错误:

  • severe errors有亿些错误
  • 找不到给出的源文件程序

4. 第一个程序

  1. 编写源程序,源程序以.asm为扩展名
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第3张图片
  2. 利用masm.exe编译1.asm生成目标文件1.obj。直接输入masm,然后如图操作
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第4张图片
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第5张图片
  3. 利用link.exe编译1.obj生成可执行文件1.exe。直接输入link,然后如图操作
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第6张图片

链接时报了个错误:no stack segment没有栈段
原因是: 在连接过程中,并未因为有“stacksg segment”,和assume了“ss:stacksg”就认为设置了堆栈段。
解决办法: 在代码段开头添加stack关键字。但是! 这样处理后,文件不能停止了,所以这个错误直接忽略吧一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第7张图片

  1. 直接1.exe运行。(输入1运行也可以哦)
    在这里插入图片描述

有简化方式:

  1. masm 文件名.asm;可以直接默认编译,不用输入参数。link也同样有这个功能。关键在于;
  2. ml 文件名.asm,编译同时链接

5. 程序执行过程

Shell壳:

  1. 操作系统是一个庞大的、复杂的软件系统
  2. 通用的操作系统,需要提供一个Shell程序,让用户能操作计算机系统
  3. DOS中的Shell,叫做command.com命令解释器。
  4. DOS启动先初始化环境,然后运行command.com,等待用户输入(命令也是一个可执行文件)
  5. 用户输入可执行文件路径,command.com会将这个程序加载入内存,设置CS/IP指向程序入口。然后command.com暂停,把CPU控制权交出。程序结束后,将控制权交给command.com,等待用户输入

下面这个过程,要烂熟于心:

1.asm
1.obj
1.exe
内存中的程序
编程:Edit
编译:masm
连接:link
加载:command
运行:CPU

6. Debug单步执行

之前的程序是没有入口的,我们编写一个有入口的(不一定用start,只要和end后面的标号相同即可,不要忘记冒号)
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第8张图片


用Debug单步执行: 用t单步执行,到了程序返回的代码int 21用p一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第9张图片一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第10张图片

补充:Debug程序运行时,cx默认记录的是程序的长度(2.EXE为c,表示12字节大小)

程序返回是返回上一级,2.asm结束返回到debug.exedebug.exe使用q命令返回到command.com


程序被加载后,CS应该和DS指向同一个地址。看上图,会发现 C S − D S = 10 H CS - DS = 10H CSDS=10H,中间那一段是什么?
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第11张图片

  1. 在内存中找一块地儿(特征是,偏移地址为0)。段地址为SA,同时也是DS的值。
  2. 弄一个程序前缀数据区PSP,来进行程序和DOS的通信(和操作系统通信的一个接口)。这个区域占256字节,也就是10H。
  3. 所以: D S = S A DS = SA DS=SA C S = S A + 10 H = D S + 10 H CS = SA + 10H = DS + 10H CS=SA+10H=DS+10H

查看PSP部分内存:
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第12张图片
查看CS指向部分内存:
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第13张图片

实验3

  1. 编写(答案在注释里)一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第14张图片
  2. 编译链接一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第15张图片
  3. 单步执行一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第16张图片一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第17张图片一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第18张图片

PSP的头两个字节确实是CD,通过查看汇编,我们可以知道,PSP的第一条语句是int 20,又是中断机制,以后再学
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第19张图片

五、[BX]和loop指令:28/77

1. debug运行程序和可执行文件写代码的不同

问题: Debug中有R命令,可以直接修改段寄存器的值。而mov只能通过通用寄存器间接修改。这两者修改有什么区别?
答: debug是程序,而mov是汇编语句,两者没关系!汇编中,只能通过jmp来跳转程序。


  1. 先来一段程序: 一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第20张图片
  2. 查看内存:
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第21张图片
  3. 怎么回事? 会发现中括号没被识别一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第22张图片
  4. 查看源代码: 果然没识别一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第23张图片

2. 内存单元&描述性符号

要完整描述一个内存单元,需要两种信息:

  1. 内存单元地址 DS:[偏移地址]
  2. 内存单元长度(类型)
  • 补充: mov ax, [0],传递的是字。mov al, [0],传递的是字节。
  • 重要:五.1所示,[0]这个不会被识别为内存单元,需要使用:
    mov bx, 0
    mov ax, [BX]
    也就是拿bx中转

描述性符号1: ()用来描述一个地址存放的内容。段寄存器:偏移地址就相当于一个指针,它指向的内存地址的值也可以用()描述。

注意: ax之类通用寄存器,本身保存的内容可以说是一个值,也可以说是一个地址。用()描述会得到这个寄存器本身保存的内容。寄存器更像是一个普通变量。
段寄存器虽然名义上是保存地址的,实际上保存的还是一个值。DS:0000可以描述一个内存单元,(DS):0000也可以描述一个内存单元。
上面的地址描述不规范,应当为(DS)*16 + 0000

  • 可以简单粗暴的理解为()就是取值的意思

描述性符号2: idata表示常量

3. Loop指令

格式: loop 标号
执行loop会进行两步操作:

  1. (cx) -= 1
  2. 如果(cx)==0,跳到标号处执行;否则向下执行

3.1 用循环计算10次方

通常,我们用loop进行循环,cx中存放循环次数
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第24张图片
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第25张图片


循环结构:

mov cx, 循环次数
s: 循环程序段
	loop s

注意: 写程序前,要小心数据溢出,要事先估算数据范围

3.2 值得注意的点&调试

5.3节代码:
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第26张图片

注意: 汇编程序中,程序不能以字母开头


一个问题: 一次t只能走一步,如果循环的次数多,那是不是要一直搞t?有没有简便方法?有的,g命令(会将指定偏移地址前的代码执行完毕)
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第27张图片
p命令,可以后台静默执行循环:
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第28张图片

4. Debug和汇编编译器masm对指令的不同处理

  1. Debug默认数据16进制;masm默认数据10进制
  2. Debug用[0]来取内存单元的值;masm用[bx]来取内存单元的值,或DS:[0]来取

段前缀: 利用DS:[0]来取内存单元的值,这个DS叫做段前缀。也可以使用es/ss/es等其他方式指定段前缀。不写的话,默认段前缀为DS。

5. 安全的内存空间

内存空间不止我们写的程序在用,还有包括操作系统在内的程序在使用。所以,我们在操作一个空间时,需要先知道它是安全的

  • 我们在面临一个选择:在操作系统中安全、规矩的编程。还是利用汇编,直接去探查硬件真相?(我选后者,直接干硬件!)

补充: 在纯DOS方式(实模式)下,可以不理会DOS,直接用汇编去操作硬件


一般情况下,0:200~0:2ff这个空间不被其他程序使用,是安全的。
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第29张图片
安全的空间不够了咋办: 只要是合法的请求,系统会给的。具体怎么给,日后再说。

6. 复制内存内容

一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第30张图片一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第31张图片

实验4答案: 0020H, 40H
看寄存器是多少字节的。

六、包含多个段的程序:31/77

为什么要使用多个段: 为了日后的封装

1. 程序的入口

我们先来看一个程序:
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第32张图片

  • dwdefine word,定义字型数据(db定义字节数据)

  • 段地址: 这八个数据存在哪儿呢?因为是放在cs代码段中,所以数据存在cs代码段中

  • 数据的偏移地址: 从0开始,0、2、4…
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第33张图片

  • 代码的偏移地址: 从16开始
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第34张图片

程序如何知道从16开始执行呢?可以注意到我们把程序的入口定义在了第一条程序语句前,就是这个入口。
最关键的不是读错行,而是读错指令。可以试下,当没有指定程序入口时,大概率也不会读错。8086很聪明,它知道从第16条开始执行。但是!因为实际读取的是机器码,从而导致语句错误(直观就是,用u查看我们所写代码对应的内存,会发现这些指令和我们写的不一样。原因就是被CPU把机器码错误组合导致的。当然,有概率不会出错。因为本人写的没出错,所以就没截图了。)总结:不一定报错,但是结果一定错


程序怎么知道程序的入口: 直接去找endend后面的标号是一个地址,程序直接去找这个地址执行

2. 程序中使用栈

需求: 把数据存入栈,然后逆序排列

  • 代码: 注意阅读注释一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第35张图片

补充: 可以看出,汇编语言并没有区域注释。这一点上和C如出一辙。

  • 查看内存: 一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第36张图片
  • 程序执行完后,查看内存:
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第37张图片

补充: 我们可以说,定义了八个字形数据。也可以说,开辟了八个字的内存空间,然后在里面存放了数据。两种角度,表达不同,理解不同,效果一样

检测点6.1

  1. mov CS:[bx], ax。题目的意思是,内存的数据在0:0 15,程序的数据在CS:0 15中,需要用内存的数据把程序的数据换了。
  2. CS
    0020H(当然,也可以写32)
    pop CS:[bx](有的地方会说这里的答案是SS:[bx]。注意,虽然SS的值等于CS,但是从逻辑上不符合题意。题意是用栈作中介,改变程序的数据。当然了,合理利用栈溢出是一种灵活性,所以我才说是注意,没说是错误)

3. 在程序中使用不同的段

前面我们在程序中放入了数据、栈、代码,我们需要时刻注意内存空间是数据、栈、还是段

  1. 这样的程序未免过于混乱
  2. 以8086为例,如果 数据段+栈段+代码段>64K ,那么一个段就放不下了
  3. 程序耦合性高。如果需要把8个字数据,变成9个字数据呢?是整体右移?还是在内存中再开辟一块,然后利用代码进行逻辑上的联结?显然,两种方法都可行,但是牵一发而动全身

代码: 注意阅读注释

补充1: 可以看出,每个部分占用一个段。(默认是连续的三个段)
一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第38张图片原因: 如果一个数据 占用N个字节,程序加载后,该段实际占有的空间是16*(N/16 + 1)

补充2: 一个段的时候,DS比CS小10H,大的部分用来放PSP了。多个段的时候,DS只比CS小1H,那么PSP在哪呢?经过本人试验,发现PSPDS - 10H处。

注意: CPU默认把机器码当指令而不是数据(数据就直接读写,指令需要CPU运算)

补充3: CPU是从上往下读的。先定义数据段,DS就小;先定义代码段,CS就小。

补充4: 如果不指定程序入口,CPU会从上到下,把碰到的机器码都当成指令

实验5

  1. 没什么好说的
  2. 如果一个数据占用N个字节,程序加载后,该段实际占有的空间是16*(N/16 + 1)
  3. CPU是从上往下读的。先定义数据段,DS就小;先定义代码段,CS就小。
  4. 如果不指定程序入口,CPU会从上到下,把碰到的机器码都当成指令
  5. 可以利用多个段寄存器,如图
    一周干掉汇编语言 #Day2 #第一个程序 #[BX]和loop #包含多个段的程序_第39张图片
  6. 没什么好说的

你可能感兴趣的:(计算机基础)