0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

简单聊聊什么是段

程序员cxuan 来源:程序员cxuan 2023-02-20 09:31 次阅读

这是 x86 汇编连载系列第六篇文章,前五篇文章见文末。

一个小细节

从开始到现在我们接触到了两种汇编指令的编写方式,一种是在 dosbox 上的 debug 模式下通过 debug -a 的方式来编写,如下图所示:

8a6df0ba-b0b2-11ed-bfe3-dac502259ad0.png

这种方式能让你在 dosbox 中直接编写汇编代码,简单直接,不需要写伪指令,方便快捷。

还有一种方式需要我们在 dosbox 外部编写汇编源文件,源文件中的代码经由 MASM 汇编编译、LINK 指令链接后一种,如下图所示:

8a8aa462-b0b2-11ed-bfe3-dac502259ad0.png

乍一看这两种方式编写的汇编源代码应该都能正确的执行,于是我们分别用两种不同的方式写下了

8aaca7e2-b0b2-11ed-bfe3-dac502259ad0.png

这几条指令。这三条指令很简单,我们的目的很明确,我们想把内存地址为 ds:[1], ds[2], ds[3] 的数据分别送入 al,bl,cl 寄存器。下面我们执行一下:

使用 debug 方式的截图如下:

8ac43b78-b0b2-11ed-bfe3-dac502259ad0.png

如图所示,在使用 debug 方式中,"[ ]" 内的指令会被直接当做内存地址进行 mov。

使用 masm 编译器方式的截图如下:

8adea3c8-b0b2-11ed-bfe3-dac502259ad0.png

如图所示,当我们使用 MASM 进行编译和链接后,[ ] 号中的 1 会被直接编译为数值 01,而不是 [1] 这个内存地址。这个是编译规定,大家要记住这个细节。

也就是说,诚如 [idata] 这种形式,debug 和 masm 汇编器对其有不同的解释,debug 认为 [1] 中的就是一个内存地址,而 masm 认为 [1] 就是一个 idata 立即数。

话又说回来了,如果我们想在汇编源文件中表示内存地址,该怎么办呢?

这就需要借助一个寄存器了 --- bx。

比如下面这段汇编代码

8b01f364-b0b2-11ed-bfe3-dac502259ad0.png

首先将 ds 寄存器设置为 2000 ,也就是 ds = 2000h,然后把 0 放入 bx 中,最后的 mov al,[bx] 就会默认从内存地址ds:[0] 处提取数据进行移动。

这样当然是可以的,不过仍然比较繁琐,我们不想要每次 mov 内存数据还要经过 bx 中转,我们想要像 debug 那样直接 mov ,该怎么做呢?其实也比较简单,直接显示指出段寄存器:[内存偏移]即可。

看下面这段汇编代码:

8b1cc1c6-b0b2-11ed-bfe3-dac502259ad0.png

如果你想要通过 MASM 的方式来取得 ds:[0] 处内存地址的话,就需要显示指定段寄存器,如果没有显示指定的话,默认按照 01 数值来处理。

所以我们可以总结一下上面所探讨的内容(基于 MASM 汇编编译器下)

mov al,[0] :将数值 0 送入 al 寄存器中,(al) = 0。

mov al,ds:[0]:(al) = ((ds) * 16 + 0) , 将内存单元中的数据送入 al 中,段地址为 ds。

mov al,[bx]:(al) = ((ds) * 16 + bx) , 将内存单元的数据送入 al 中,段地址为 ds。

mov al,ds:[bx] :和 mov al,[bx] 含义相同

还可以更为精简的总结一点:

MASM 汇编编译器会将 [idata] 编译为 idata,若想访问内存地址,则必须显示指定段地址或者使用 bx 进行中转。

上面这些内容在本人其他文章中已经涉及到了,不过讲的不太细致,这篇文章算是细致的讲了下。

段前缀

上面的内容多次提到了一个名词就是 段,段所表示的其实也是一段内存空间,不过这种划分的方式是由 CPU 来决定的,内存并不会分为多个段。段的划分是主要为了 CPU 能够更方便的寻址,要想寻找段内的每个地址和数据,都需要有两个概念:段基址和段内偏移。

在汇编语言中,一般通过 [bx] 来给出偏移地址,它的段基础在 ds 中,ds 是默认的段寄存器。

不过,只有一个 ds 段显然是无法应对复杂程序的寻址方式的,所以还可能会有多个段,如下所示:

8b37e7f8-b0b2-11ed-bfe3-dac502259ad0.png

上面列举了四种不同的段寄存器和寻址方式。

第一条指令把段基址为 ds,偏移地址为 bx 的内存地址的内容送入 ax ,长度为 2 个字节单元,也就是一个字,16 位。

第二条指令把段基址为 cs,偏移地址为 bx 的内存地址的内容送入 ax ,长度为 2 个字节单元,一个字,16 位。

第三条指令把段基址为 ss,偏移地址为 bx 的内存地址的内容送入 ax ,长度为 2 个字节单元,一个字,16 位。

第四条指令把段基址为 es,偏移地址为 bx 的内存地址的内容送入 ax ,长度为 2 个字节单元,一个字,16 位。

由于 ds、cs、ss、es 都是显示指出的,所以 ds、cs、ss、es 又被称为段前缀。

一段安全的存储空间

我们写出的程序经过编译连接后,会由操作系统分配内存空间,我们并不知道哪些内存空间是有用的,哪些内存空间是保留的,哪些内存空间是可以使用的,由于有些内存空间存储着重要的系统数据或代码,所以我们最好不要随意的向内存空间写入数据,这是很危险的,比如下面这几条指令:

8b4daac0-b0b2-11ed-bfe3-dac502259ad0.png

之前为了方便,我们没有判断 1000:[0] 这个内存空间有没有存放重要代码或数据就将数据写入其中,这种做法是错误的,如果 1000:[0] 处刚好存放着文件系统的起始代码,那么 mov ds:[0],al 就会将其改写,引发系统崩溃。

再看一段程序:

8b67ed54-b0b2-11ed-bfe3-dac502259ad0.png

我们编写好代码后,进行编译链接,debug 这段代码:

8b80817a-b0b2-11ed-bfe3-dac502259ad0.png

当我们执行完 mov ds:[26h],ax 后,说什么也执行不下去了。

并不是我不想执行了,而是系统不让我执行了,因为系统死机了。。。。。。大家可以试试。

所以,在不清楚这段内存空间是干什么的时候,最不好要随意向内存空间写入数据。由于内存空间是由操作系统直接分配的,所以要想向一段内存空间写入数据的话,要使用操作系统给我们分配的内存空间。

那么话又说回来了,操作系统给我们分配了哪些空间可以安全的写入数据呢?

在一般的 PC 机,DOS 方式下,DOS 和其他合法程序一般都不会使用0:200 ~ 0:2ff(00200h ~ 002ffh)这段 256 个字节的空间,可以认为这段内存区域是安全的。

不过为了谨慎起见,我们写入的时候,最好使用 debug -d 来看一下这段内存区域有没有存储数据。

段前缀的使用

考虑一个问题,如何将内存 ffff:0 ~ ffff:b 单元中的数据复制到 0:200 ~ 0:20b 单元中?

需要考虑以下几点:

0:200 ~ 0:20b 其实就是 200:0 ~ 200:b ,这就是对同一段内存空间的两种不同的描述。

上面是两段不同的内存空间,所以需要两个段基址,通过一个寄存器 dl 来进行中转,把 ffff:0 ~ ffff:b 地址空间的数据复制到 dl 中,然后把 dl 中的数据再复制到 0:200 ~ 0:20b 中。

一共复制 (b - 0) + 1 = 12 次。

开码!

8ba2a192-b0b2-11ed-bfe3-dac502259ad0.png

从上面代码可以看到,我们显示使用了两种段前缀 ds 和 es ,这就是一个段前缀的使用案例。

我们分别将 0ffffh 和 200h 赋给了 ds 和 es 寄存器,然后设置 cx 循环次数为 12 次,s 是一个伪指令,表示循环的开始处,每个循环中都会把 0ffff:[bx] 中的数据赋值给 dl ,因为这是一个内存地址,所以使用 8 位寄存器就可以接收,然后将 dl 中的数据赋值给 200:[bx] 处,再执行循环。

审核编辑 :李倩

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5310

    浏览量

    119983
  • 代码
    +关注

    关注

    30

    文章

    4742

    浏览量

    68332
  • 编译器
    +关注

    关注

    1

    文章

    1618

    浏览量

    49045

原文标题:简单聊聊什么是段

文章出处:【微信号:cxuangoodjob,微信公众号:程序员cxuan】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    简单聊聊什么是功率因数

    在交流系统中,电源提供正弦电压波形,负载则主导电流波形。在加热器或白炽灯泡等最简单的“阻性”负载中,电流波形与电压波形形状一致,并且相位(时间轴)也保持一致。
    发表于 07-14 16:55 1064次阅读
    <b class='flag-5'>简单</b>的<b class='flag-5'>聊聊</b>什么是功率因数

    高通产品的进来聊聊

    高通产品的进来聊聊,共享一下资源.
    发表于 01-03 14:32

    为什么加入程序code会反而减小呢?

    今天在调试程序的过程中发现个挺有趣的问题:请大神们过来讨论一下,我就不选最佳了,让系统自己选。毕竟是跟大神们聊聊天而已。为什么加入程序code会反而减小呢?能说下真实理解么
    发表于 06-16 09:03

    聊聊CMSIS

    这次我们来聊聊CMSIS。之前在Kile环境下创建STM32工程的时候,对有些文件的加入总不是很了解,书上或网上建立工程的教程对于这些文件的加入也是一笔带过,或者直接不说。对于类似名叫
    发表于 08-24 07:50

    聊聊对按键扫描软件结构的理解

    按键扫描,我想应该是比较简单的单片机应用了,但是有时候看起来简单的东西反而不好写。本文拿大部分人觉得简单的按键扫描聊聊我工作至今对于软件结构的理解。嗯,对的,是结构,不是架构,暂时不敢
    发表于 12-02 06:27

    聊聊基于STM32F103的红外循迹避障小车的Proteus仿真

    红外循迹及红外避障实现较简单,无论是51单片机还是STM32单片机,其例程随处可见。但是完全可以运行的Proteus仿真,开源的并不多,更不要说基于STM32单片机的仿真。下面跟大家聊聊基于STM32F103的红外循迹避障小车的Proteus仿真。
    发表于 01-05 06:46

    聊聊复位电路

    时钟电路我第一篇博客已经说讲过了,今天我们来聊聊复位电路。当然,复位电路博大精深,并...
    发表于 01-17 07:50

    聊聊存储器的相关知识

    虚拟地址物理地址等众多地址及MMU相关知识先聊聊存储器STM32单片机存储器关于编译器生成的文件数据在存储器上的存储结构物理地址、虚拟地址、线性地址和逻辑地址物理地址虚拟地址逻辑地址线性地址这些地址
    发表于 02-11 07:51

    码LCD驱动简单原理是什么

    时钟的冒号“:”,这样如果使用IO口直接扫描显示,则会减小PCB面积,降低成本。但是,本方案不合适驱动太多的(占用IO太多),也不合适非常低功耗的场合。码LCD驱动简单原理:如图1所示。LCD是一种...
    发表于 02-18 07:54

    聊聊光敏传感器最简单的使用

    描述的可能也有不清楚的地方,有问题的小伙伴可以私聊我,一起学习共同进步。今天发现了一个很有意思的传感器——光敏传感器,它可以判断光线的亮暗,当然亮暗是个相对的说法,它可以自己设定阈值,今天来聊聊
    发表于 02-24 07:50

    简单聊聊伺服电机启动电流的问题

      现在EPU和EMA应用越来越广泛,作为液压领域的从业者,对电机有个基本的了解是有必要的。  今天简单聊聊伺服电机启动电流的问题。  1 电机启动电流相比于正常工作电流是大是小?为什么?  2
    发表于 03-16 17:38

    一文了解多个的相关程序

    上回我们简单认识了一下什么是前缀和一安全的空间是哪里,但是程序中不会仅有一个,复杂程
    的头像 发表于 03-08 14:28 572次阅读

    简单聊聊MCAL的最小工程

    英飞凌的芯片在汽车电子里用得可谓是颇多,最近刚好在摸TC3系列的CAN模块,来简单聊聊MCAL的最小工程。
    的头像 发表于 03-21 09:25 2563次阅读

    聊聊Redis的使用案例

    今天我们来聊聊 Redis 的使用案例。
    的头像 发表于 12-13 14:13 485次阅读

    聊聊半导体产品的8大封装工艺

    今天我们聊聊半导体产品的封装工艺,一提到“封装”,大家不难就会想到“包装”,但是,封装可不能简单的就认为等同于包装的哦
    的头像 发表于 02-23 14:42 3000次阅读
    <b class='flag-5'>聊聊</b>半导体产品的8大封装工艺