小甲鱼零基础汇编语言学习笔记第六章之包含多个段的程序

在前面的几个章节中,我们的程序都是只有一个代码段,本章我们开始学习如何编写包含多个段的程序。

1、在代码段中使用数据

首先考虑这样一个问题,计算以下8个数据的和,结果存放在ax寄存器中:

0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

在前面的课程中,我们都是累加某些内存单元中的数据,并不关心数据本身,可现在我们要累加就是已经给定了数值的数据。

代码如下:

 1 assume cs:codesg
 2 codesg segment
 3      dw  0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
 4      mov bx,0
 5      mov ax,0
 6      mov cx,8
 7 s:  add ax,cs:[bx]
 8      add bx,2
 9      loop s
10      mov ax,4c00h
11      int 21h
12 codesg ends
13 end

在该程序第一行中的"dw"的含义是定义字型数据,即define word。在这里,我们使用dw定义了8个字型数据(数据之间用逗号分隔),它们所占的内存空间大小为16个字节。

将代码进行编译,连接后,使用debug进行调试,我们可以看到,在地址0B4FH:0000这个地址单元中,存放了我们定义的字型数据:

在0B4FH:0010开始的地址单元中,存放了我们的指令:

但是我们查看0B4F:0000这个地址单元时,它却并没有存放我们的指令(按理说应该是会存放我们的指令的,但是我们的指令却是存放在0B4FH:0010开始的地址单元中):

出现这样的原因是,cpu读取的是指令所对应的机器码,从0B4F:0000开始的地质单元中,存放的是由数据0123h、0456h等我们预先定义好的字型数据转换成机器码所对应的指令,这样造成了cpu的误读。要解决这个问题,我们就应该给程序指定一个入口,而不是直接从定义字型数据的指令开始,所以程序应该改为如下所示:

 1 assume cs:codesg
 2 codesg segment
 3       dw  0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
 4 start:mov bx,0
 5         mov ax,0
 6         mov cx,8
 7
 8 s:     add ax,cs:[bx]
 9         add bx,2
10         loop s
11
12    mov ax,4c00h
13    int 21h
14 codesg ends
15 end start

我们重新编译连接这个程序,然后使用debug调试后,使用r命令查看寄存器的情况,可以发现,cs:ip默认就指向了程序的第一条指令,这是因为我们在程序中定义了一个程序的入口,但是程序是根据最后的end来查找程序的入口的,这一点要特别提醒一下。debug调试如图所示:

2、在代码段中使用栈

首先我们还是考虑一个问题,利用栈将程序定义的数据逆序存放,这个程序的代码应该如何来写?

当我们看到逆序存放这样的字眼的时候,就应该首先想到栈,应为要实现逆序,就必定会用到栈“LIFO”这个特性(后进先出)。所以程序的代码如下所示:

 1 assume cs:codesg
 2 codesg segment
 3      dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
 4      dw 0,0,0,0,0,0,0,0 ;这里用dw定义8个字型数据,在程序加载后,将取得8个字的内存空间,存放这8个数据,我们在后面的程序中         ;就将这段空间当做栈来使用。
 5
 6 start:mov ax,cs
 7         mov ss,ax
 8         mov sp,32 ;这里设置栈顶ss:sp指向cs:32
 9         mov bx,0
10         mov cx,8
11 s:     push cs:[bx]
12         add bx,2
13         loop s ;这里是将代码段0~16单元中的8个字型数据依次入栈
14
15         mov bx,0
16         mov cx,8
17  s0:  pop cs:[bx]
18         add bx,2
19         loop s0 ;这里依次出栈8个字型数据到代码段0~16单元中
20
21         mov ax,4c00h
22         int 21h
23
24 codesg ends
25 end start

在编译连接这段程序后,我们使用debug来查看程序的运行情况:

我们是用t和d命令,来看看执行指令后,内存单元中的存储情况:

此时使用d命令来查看内存的情况:

3、将数据、代码、栈放入不同的段中

在前面的程序中,我们将数据、代码、栈放在了同一个段中,这样使程序显得很混乱。假如程序中的数据、代码、栈不大的话,这样做没问题,但是程序变大后,程序将变得很难看。在8086CPU中,如果数据、代码、栈的空间需求大于了64kb的话,这三者就不能存放在同一个段中,需要分开来存放。那么应该如何定义多个段呢?

我们使用定义代码段一样的方法来定义多个段,在这些段里面定义需要的数据,或通过定义数据来取得栈空间。现在我们还是考虑同样的一个问题,将栈中的数据逆序存储到一个寄存器中,代码入下所示:

 1 assume cs:code,ds:data,ss:stack
 2
 3 data segment
 4      dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
 5 data ends
 6
 7 stack segment
 8       dw 0,0,0,0,0,0,0,0
 9 stack ends
10
11 code segment
12      start:mov ax,stack
13              mov ss,ax
14              mov sp,16 ;设置ss指向stack,设置ss:sp指向stack:16,cpu执行完这些指令后,就将stack段当做一个栈空间来使用。
15              mov ax,data
16              mov ds,ax ;想要ds:bx访问数据段,ds就要指向ax
17              mov bx,0
18              mov cx,8
19         s:  push [bx]
20              add bx,2
21              loop s
22
23              mov bx,0
24              mov cx,8
25       s0:   pop [bx]
26               add bx,2
27               loop s0
28 code ends
29 end start

在上面的代码中,我们定义了数据段data,栈段stack,代码段code,分别用ds,ss,cs指向这些段。这样做,看上去整个程序非常清晰明了。

CPU如何处理我们定义的段中的内容,是当做指令来执行,当做数据访问,还是当做栈空间来使用,完全是靠程序中具体的汇编指令和汇编指令对CS:IP、SS:SP、DS等寄存器的设置来决定的。

时间: 2024-12-23 11:25:19

小甲鱼零基础汇编语言学习笔记第六章之包含多个段的程序的相关文章

小甲鱼零基础汇编语言学习笔记第四章之第一个汇编程序

1.使用工具 代码编写:NotePad++,代码编译器:MASM 2.一个源程序从写出到执行的过程 第一步,使用文本编辑器,用汇编语言编写汇编源程序,这一步产生一个存储源程序的文本文件. 第二步,对源程序进行编译,连接.使用MASM.exe对源程序文件进行编译,产生目标文件,再使用LINK.exe对目标文件进行连接,生成可在操作系统中直接运行的可执行文件.可执行文件包括两个部分,程序(从源程序的汇编指令翻译过来的机器码)和数据(源程序中定义的数据):相关的描述信息 (程序有多大,要占多少内存空间

小甲鱼零基础汇编语言学习笔记第五章之[BX]和loop指令

这一章主要介绍什么是[BX]以及loop(循环)指令怎么使用,loop和[BX]又怎么样相结合,段前缀又是什么鬼,以及如何使用段前缀. 1.[BX]的概念 [BX]和[0]类似,[0]表示内存单元的偏移地址是0.要完整描述一个内存单元,需要两种信息:内存单元的地址,内存单元的长度(类型).[BX]同样也表示一个内存单元,它的偏移地址在bx中,比如指令:mov ax,[bx].这里我们以一个程序为例: 1 assume cs:codesg 2 codesg segment 3 start: mov

小甲鱼零基础python课后题 P22 021函数:递归是神马

0.递归在编程上的形式是如何表现的呢? 答:在编程上,递归表现为函数调用本身这么一个行为. 1.递归必须满足哪两个基本条件? 答:1函数调用自己. 2有正确的返回条件 2.思考一下,按照递归的特性,在编程中有没有不得不使用递归的情况? 答:不知道呢,应该就是小甲鱼课上说的那个三角形 3.用递归去计算阶乘问题或奜波那契数列是很糟糕的算法,你知道为什么吗? 答:效率太低,每次都需要调用自己,占用空间. 4.请聊一聊递归的优缺点.(想怎么写就怎么写按自己理解的来) 答:优点,代码简洁 缺点 ,效率低占

Java学习笔记—第六章 流程控制语句

第六章  熟悉Java的流程控制语句 Java的程序流程控制分为顺序结构.选择结构.循环结构和跳转语句. 顺序结构:按照程序代码自上而下执行,直到程序结束,中间没有任何判断和跳转. 选择结构(分支结构):判断给定的条件,根据判断结果控制程序的流程.包括if语句和switch语句. 2.1 if语句:通过判断给定表达式的值来决定程序的流程.常见if语句的形式有三种: (1)if(expression){ statement: } (2)if(expression){ statement; }els

小甲鱼零基础入门PYTHON

 000.愉快的开始 00:17:37 ☆  001.我和Python的第一次亲密接触 00:13:26 ★  002.用Python设计第一个游戏 00:24:00 ★  003.小插曲之变量和字符串 00:19:04 ★  004.改进我们的小游戏 00:26:31 ★  005.闲聊之Python的数据类型 00:18:41 ★  006.Pyhon之常用操作符 00:16:31    007.了不起的分支和循环 00:16:30    008.了不起的分支和循环2 00:12:09  

小甲鱼零基础python课后题 P20 019函数:我的地盘听我的

测试题 0.如果希望在函数中修改全局变量的值,应该使用什么关键字? 答:globe 1.在嵌套函数中,如果希望在内部函数修改外部函数的局部变量,应该使用什么关键字? 答:nonlocal 2.python的函数可以嵌套,但要注意访问的作用域问题哦,请问以下代码存在什么问题呢? def outside(): print("I am outside") def inside(): print("I am inside") inside() 答:inside() 不是外部

<<Python基础教程>>学习笔记 | 第10章 | 充电时刻

第10章 | 充电时刻 本章主要介绍模块及其工作机制 ------ 模块 >>> import math >>> math.sin(0) 0.0 模块是程序 一个简单的模块 #hello.py print ("Hello,World!") >>> import hello Traceback (most recent call last): File "<pyshell#56>", line 1, i

&lt;&lt;Python基础教程&gt;&gt;学习笔记 | 第09章 | 魔法方法、属性和迭代器

这一章,有点抽象,看着有点蛋疼! 双下划线__future__或单下划线有特殊含义,在Python中,这些名字的集合称为魔法方法:最重要的是__init__和一些处理访问对象的方法,这些方法允许你创建自己的序列或者是映射. ------ 准备工作: 将__metaclass__=type放在模块的最开始位置,以确保类时最新式的.考虑下面两个类 class NewStyle(object): more_code_here class OldStyle: more_code_here 如果文件以__

&lt;&lt;Python基础教程&gt;&gt;学习笔记 | 第13章 | 数据库支持

备注:这章内容相对介绍的比较简单,不过例子比较使用,主要是要掌握如果连接,使用数据库,并以SQLite做示例 ------ Python数据库API 为了解决Python中各种数据库模块间的兼容问题,现在已经通过了一个标准的DB API.目前的API版本(2.0)定义在PEP249中的Python Database API Specification v2.0中. 异常 为了尽可能准确地处理错误,API中定义了一些异常.它们被定义在一种层次结构中,所以可以通过一个except块捕捉多种异常. 连