前言
在汇编语言中,合理地管理数据和栈是编写高效程序的重要一环。本文将探讨在代码段使用数据和在代码段使用栈的两个关键概念。代码段是存储程序指令的地方,而数据段和栈则是用来存储程序运行时所需的数据和执行过程中的临时信息。通过深入了解这两者的使用方式,我们可以更好地优化程序的性能和可读性。
一、8086中的es寄存器
1.1 es寄存器是什么
在8086 CPU中,ES 寄存器是一个特殊的寄存器,全称为 Extra Segment Register(附加段寄存器)。它的主要作用是帮助CPU找到程序中一些特定数据的家庭地址。
想象一下你住在一个大公寓楼里。楼房号是段地址,而你的家门号是偏移地址。ES 寄存器就像是告诉你的朋友,你住在哪个大楼里。当你朋友来找你时,先知道了楼房号(段地址),然后再通过你提供的门牌号(偏移地址)就能准确地找到你的家。
在实际编程中,ES 寄存器通常用于在处理字符串、数组等数据时,指示这些数据所在的段地址。这样,CPU可以更方便地访问并处理这些数据。
1.2 ds和es寄存器
大家会不会疑惑,前面不是学过ds为段地址吗,为什么又来一个es,其实他们不一样
在8086 CPU中,DS 寄存器和 ES 寄存器都是段寄存器,但它们各自有不同的主要用途。
DS 寄存器(Data Segment Register)通常用于指向程序的数据段,存储程序中常用数据的地址信息。当程序需要访问变量、数组或其他数据时,DS 寄存器提供了这些数据所在段的地址。
ES 寄存器(Extra Segment Register)则是一个额外的段寄存器,通常用于协助处理一些特殊操作,比如在一些字符串操作中,ES 可以被用来指示额外的目标段地址,以便更便捷地进行数据传输或操作。
虽然 DS 和 ES 寄存器都是用来存储段地址,但它们的使用场景不同。DS 主要指向程序的数据段,而 ES 在一些特殊操作中提供了额外的段地址信息,帮助程序更有效地执行某些任务,比如字符串处理、内存操作等。
那么他们的操作是一样的
二、在代码段使用数据
2.1 一个危险的问题
例:将内存ffff:0~ffff:b中的数据拷贝到 0:200~0:20b单元中。
问题
程序中直接写地址,危险!
“安全”位置存放数据,存哪里?
对策
在程序的段中存放数据,运行时由操作系统分配空间。
段的类别:数据段、代码段、栈段
各种段中均可以有数据
可以在单个的段中安置,也可以将数据、代码、栈放入不同的段中。
应用案例
问题:编程计算以下8个数据的和,结果存在ax 寄存器中
0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
解决方案1:
assume cs:codesg codesg segment dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h mov bx,0 mov cx,8 mov ax,0 s:add ax,cs:[bx] add bx,2 loop s mov ax,4c00h int 21h codesg ends end start
dw: define word,
定义字型数据
dw 定义一个字
db 定义一个字节
dd 定义一个双字
这个程序有问题!
但是很遗憾,这个程序还是有问题,
我们使用u命令查看他,发现第一个不是mov bx,0,而是变成其他的了,和我们之前学的不符
解决问题的关键:让数据从CS:0000开始,让代码从CS:0010开始
这样改进
解决方案2
定义一个标号,指示代码开始的位置。
效果:程序加载后,CS:IP指向要执行的第一条指令在start处!
assume cs:codesg codesg segment dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h start: mov bx,0 mov cx,8 mov ax,0 s:add ax,cs:[bx] add bx,2 loop s mov ax,4c00h int 21h codesg ends end start
start即是标号,在end后面加上标号
然后cs就会先指向start这里了,我们在程序中使用还是从0000:0开始使用
三、在代码段使用栈
3.1 在代码段中使用栈:以数据逆序存放为例
问题
完成下面的程序,利用栈,将程序中定义的数据逆序存放。
assume cs:codesg codesg segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h ? code ends end
程序的思路大致如下:
程序运行时,定义的数据存放在cs:0~cs:F单元中,共8个字单元。
依次将这8个字单元中的数据入栈,然后再依次出栈到这 8 个字
单元中,从而实现数据的逆序存放。
栈需要的内存空间,在程序中通过定义“空”数据来取得。
数据逆序存放程序
assume cs:codesg codesg segment dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 start: mov ax,cs mov ss,ax mov sp,30h ; 入栈 ; 出栈 mov ax,4c00h int 21h codesg ends end start
我们可以写出上面这个程序,
ss和sp指定栈的段地址和偏移地址,因为一开始的8个字占了16个字节,所以把栈放到30h,不要重复了
入栈代码
mov bx,0 mov cx,8 s: push cs:[bx] add bx,2 loop s
出栈代码
mov bx,0 mov cx,8 s0: pop cs:[bx] add bx,2 loop s0
四、将数据、代码、栈放入不同的段
4.1 评价这种方案
assume cs:codesg codesg segment dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 start: mov ax,cs mov ss,ax mov sp,30h ; 入栈 ; 出栈 mov ax,4c00h int 21h codesg ends end start
这种方式固然是可以的,当数据量小的时候可以用,但是当数据量大的时候,我们就需要把他们放在不同的段里面。
4.2 把数据、代码、栈放入不同的段
我们使用下面这种格式声明一个段:
段的名称 segment 数据... 段的名称 ends
4.3 进行改进
逆序读取就可以变成下面这样
我们可以使用段的名称来告诉他段的地址
比如:mov ax,stack
就是把stack的段地址放入ax中
assume cs:codesg codesg segment data segment dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h data ends stack segment dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 stack ends start: mov ax,stack mov ss,ax mov sp,20h mov ax,data mov ds,ax mov bx,0 mov cx,8 s: push [bx] add bx,2 loop s mov bx,0 mov cx,8 s0: pop [bx] add bx,2 loop s0 mov ax,4c00h int 21h codesg ends end start
总结
在代码段使用数据和使用栈是汇编程序中的两个重要概念。合理地在代码段中存储常量和初始化数据,以及灵活地利用栈来管理函数执行过程中的数据,都是优化程序性能和可读性的有效手段。深入理解这两个概念,有助于编写更高效、清晰的汇编代码。希望本文能够帮助读者更好地理解在代码段使用数据和使用栈的原理和实践。