引言

在上篇博文中,我们学习了 [bx+si] 的灵活寻址形式,由此讲解了汇编中的多重循环实现。那么本篇博文中,我们将继续学习灵活寻址其他实现形式。

本次学习从一道编程案例开始学起。

编程示例如下:

assume cs:code,ds:datadata segmentdb '1. file         'db '2. edit         'db '3. search       'db '4. view         'db '5. options      'db '6. help         '
data ends

题目:编程实现将data段中每个单词的前四个字母改为大写字母。

题目分析

首先我们观察给出的每行数据中,前三位的数据不再是字母,也就意味着我们在进行大小写转换时,需要避开前三位,转换从第4位开始,即下标3。那么我们可以将行偏移 bx 设置为3,即从第4位数据开始访问,列偏移 si 设置为0,这样我们就只访问到每行中的单词前4个字母,然后对其进行大写转换即可。

编程实现

编写代码如下:

assume cs:code,ds:data,ss:stackdata segmentdb '1. file         'db '2. edit         'db '3. search       'db '4. view         'db '5. options      'db '6. help         '
data endsstack segmentdw 0,0,0,0,0,0,0,0
stack endscode segmentstart:mov ax,datamov ds,ax            ; 设置数据段mov bx,3             ; 设置行起始偏移为3mov cx,6             ; 设置外层循环为6次s:push cx              ; 将当前外层循环数入栈mov cx,4             ; 设置内层循环为4次mov si,0             ; 设置列起始偏移为0s1:   mov al,ds:[bx+si]    ; 取 bx+si 下的字节数据到al寄存器中and al,5FH           ; 将al寄存器中的数据与5FH进行且运算,将结果放到al寄存器中mov ds:[bx+si],al    ; 将al寄存器中的数据放到 bx+si 字节单元下add si,1             ; 列偏移加1,移到下一个字节单元loop s1              ; 判断内层循环是否结束pop cx               ; 将外层循环数出栈,放到CX寄存器内add bx,16            ; 行偏移加16,移到下一行loop s               ; 判断外层循环是否结束mov ax,4c00Hint 21H
code ends
end start

由于我们初始化将bx偏移位置设置为3,且每一行的长度都是16行,那么bx加上16后,所处的偏移位置是第二行的下标3处,这样就保证了第二行起始位置是英文单词。

我们将上述代码编译连接后,在Debug内进行运行调试,观察执行情况:

首先我们设置完数据段后,使用d命令查看当前数据段中的数据如上图,接下来我们使用t命令加p命令,结束双层循环后,查看此时数据段中的数据如下图:

可以看到,我们编码实现了题目要求,将每行中每个单词的前4个字母转换成了大写。

疑问

看到这里你可能会存在疑问,在上述编码实现中,我们使用的是 [bx+si] 形式来做的,该形式是上篇博文中的所学内容,那么本篇博文中我们是要学习新的寻址形式的,既然 [bx+si] 已经可以解决问题,还学什么新寻址呢?

其实不尽然,[bx+si] 虽然已经功能强大,但是在某些情况下,还是会面临着局限性。比如,我们将上述题目中的数据做出以下修改:

assume cs:code,ds:datadata segmentdb '1. file         'db 'edit            'db '3. search       'db '4. view         'db 'options         'db '6. help         '
data ends

题目:编程实现将data段中每个单词的前四个字母改为大写字母。

观察题目你就会发现,给出的数据中每一行并不在是一个统一的格式,其中第2行、第5行数据,与其他四行数据格式并不相同。那么此时我们使用 [bx+si] 是否还可行呢?答案是当然不行了!

如果使用 [bx+si] 寻址形式,bx的起始偏移位置就是一大难题,如果设置bx起始偏移为0的话,那么第一行、三行、四行、六行的寻址将会发生错误,毕竟它们的起始位置并不是英文字母。当然我们可以通过判断0偏移处的字节数据是否是一个字母来解决这样的问题,但是我们目前还没有学习到判断,所以就需要另外的方式来处理。

使用 [bx+si+idata]

[bx+si+idata] 也是表示一个内存单元,那么该内存单元的偏移地址为:bx中的值加上si中的值再加上一个自然数idata

指令:mov ax,[bx+si+idata]表示为:

将段地址为DS,偏移地址为 bx 中的值加上 si 中的值 再加上一个自然数 idata下的字单元数据,送入寄存器AX中。

数学化描述为:(ax) = ((ds)*16+(bx)+(si)+idata)

由于 idata 是一个固定的自然数,无法在程序运行中改变,所以 [bx+si+idata] 的寻址方式相比较 [bx+si] 的寻址方式,灵活程度并未大幅度提升。那这里你可能就要疑问了,既然灵活程度上提升不大,那为什么还要再增加一个这样的寻址呢?它的意义在什么地方?

我们知道遍历一个类二维数组的内存空间,我们使用 bx 来定位每行数据的起始地址,通过si自增,来使用 si 来遍历每行中的每列数据。那么增加的自然数 idata ,则相当于定位了列的起始地址。

示例1

就拿本篇博文开头的编程示例来说。

data segmentdb '1. file         'db '2. edit         'db '3. search       'db '4. view         'db '5. options      'db '6. help         '
data ends

我们观察发现,每一行中,第4列开始才是英文字母,在示例中我们通过设置行的起始位置bx为3来完成了寻址。这里使用 [bx+si+idata] 来做,行的起始位置bx为0保持不变,设置列的起始位置为3,即 idata 为3,那么我们照样可以完成寻址。

代码只需改动几处:

assume cs:code,ds:data,ss:stackdata segmentdb '1. file         'db '2. edit         'db '3. search       'db '4. view         'db '5. options      'db '6. help         '
data endsstack segmentdw 0,0,0,0,0,0,0,0
stack endscode segmentstart:mov ax,datamov ds,axmov bx,0              ; 设置行的起始偏移为0mov cx,6s:push cxmov cx,4mov si,0s1:  mov al,ds:[bx+si+3]   ; 偏移再加上3,确定列的起始偏移and al,5FHmov ds:[bx+si+3],aladd si,1loop s1pop cxadd bx,16loop smov ax,4c00Hint 21H
code ends
end start

该代码和上面的代码改动处就在于,首先我们将bx的值重新归0,即行的起始位置回归0;然后将 [bx+si] 改为 [bx+si+idata],其中 idata 即列的起始位置,我们设置为3,这样在寻址时就会从每行中的第四列开始,确定寻到的数据为英文字母。

我们将上述代码编译连接后,在debug中运行调试,使用d命令查看结果:

可以看到我们将前四个英文字母变成了大写,使用 [bx+si+idata] 寻址完成了要求结果。

思考

在上述示例中,我们虽然明白了 [bx+si+idata] 的使用,但是还是存在一丝疑惑,那就是 idata 似乎并没有为寻址提供了多少便利,尤其是在本篇开头的示例中,我们直接使用 [bx+si] 的寻址方式照样完成,idata 成为了可有可无的存在。那么存在 [bx+si+idata] 它到底是为了什么?

首先这里说明,在 [bx+si+idata] 寻址方式中,idata的主要功能是定位二维数组中每行中的寻址起始位置。例如上述实例中,每行的数据需要从第3位开始,所以idata值便设置为3即可。

你会反驳说这个起始位3,我们可以设置si起始为3或者去设置bx,不需要再来一个idata来凑热闹呀~当然,如果你面临的是上述的示例,你完全可以这样做没问题,只是不符合编程规范而已。

注意,汇编语言虽然是低级语言,它也是有自己的编程规范的,对于内存访问场景,规范要求应该尽量使 bx 寄存器、si 寄存器、di 寄存器等一些值初始为0,而不要赋值一个非0的初始值。

为什么要有这样的规范呢?那是因为寄存器初始值为0符合绝大多数的访问内存场景要求,如果你对此很疑惑,证明你在汇编的开发上比较少。下面,我们通过一个示例,将深入的理解 [bx+si+idata] 的使用场景,领略为何有如此规范的用意!

示例2

data segmentdb '1. file         'db '2. edit         'db '3. search       'db '4. view         'db '5. options      'db '6. help         '
data ends

编程实现将上述数据段中的,提取每行英文单词的前四个英文字母,将其转换大写后按照每行的排列顺序,写到内存起始地址20000H下。

根据题目思考,首先转换英文大小写我们已经很熟悉了,如何将转换后的数据按行写到新的地址下似乎有点难度。如果使用 [bx+si] 的寻址方式,很明显我们需要设置bx的初始值或者si的初始值为3才能正确寻址到源数据进行处理。但是这样,在写入目的地址的时候却变得很不合理,因为目的地址的起始偏移为0,如果bx的初始值或者si的初始值为3,这样将会导致无法正确写入目的地址。

根据我们的逻辑,源数据的处理和写入目的地址,是在同一个循环下进行,也就是说,寻址源地址和寻址目的地址是要使用相同的偏移地址。因为要求写入的目的地址的起始偏移位置是0,那么就意味着源地址起始偏移也是0才行,这样才保证了两边数据访问的一致性。所以,在该示例中,肯定不能将bx的初始值或者si的初始值设置为3,它俩初始值只能为0!你总不可能说在处理完源数据后,再修改一次bx、si的值来保证写入目的地址正确,这样做只会增加逻辑复杂度,同时降低程序的运行效率!

既然如此,显然使用 [bx+si] 的寻址方式是不符合要求了~使用 [bx+si+idata] 才是解决之道!

编程实现代码如下:

assume cs:code,ds:data,ss:stackdata segmentdb '1. file         'db '2. edit         'db '3. search       'db '4. view         'db '5. options      'db '6. help         '
data endsstack segmentdw 0,0,0,0,0,0,0,0
stack endscode segmentstart:mov ax,datamov ds,ax    mov ax,2000H        mov es,ax               ; 设置es段地址为2000H,做为目的地址的段地址mov bx,0                ; 因为目的地址的起始偏移位置为0,所以bx初始为0mov cx,6           s:push cx              mov cx,4             mov si,0                ; 设置每行的起始偏移为0              s1: mov al,ds:[bx+si+3]     ; 使用[bx+si+idata]寻址,设置每行的起始位置为3and al,5FH   mov es:[bx+si],al       ; 写入目的地址,偏移为bx+siadd si,1             loop s1              pop cx               add bx,16            loop s               mov ax,4c00Hint 21H
code ends
end start

我们看上述编程实现,其中关键的地方是,我们在寻址源数据的时候,通过 [bx+si+idata] 的形式设置每行的起始位置,这样保证了bx和si初始值为0,在数据写入目的地址时,就可以直接使用bx和si来做为目的地址的偏移地址,确保数据正确写入指定位置

我们将上述代码编译连接后,在debug中运行调试,使用d命令查看地址2000H:0H~2000H:5FH的数据:

可以看到我们成功将每行英文单词的前四个因为字母转换大写后,将其按照每行的排列顺序写到了指定目的地址下。

总结

[bx+si+idata] 的寻址方式,灵活性体现在:

它通过一个自然数idata来指定在二维数组的寻址中每行的起始位置,使bx、si\di 寄存器无需赋值一个非零初始值,这样保证了在数据复制场景中,源地址和目的地址的偏移地址一致性,提高了内存访问效率。

通过示例2中的编程展示,相信你已经意识到了 [bx+si+idata] 存在的意义,后面在面临复杂的访问内存场景,亦能做到选择适合的寻址方式来处理。

本篇结束语

在本篇博文中,我们学习了一个全新的灵活寻址方式:[bx+si+idata],了解了idata在其的作用和含义,通过具体的示例分析和与 [bx+si] 寻址方式的对比,深入刨析了 [bx+si+idata] 寻址方式的意义所在。

截止到现在,我们已经学习了众多灵活寻址方式,有[bx]、[bx+idata]、[bx+si]、[bx+si+idata] 等等,那么在下篇博文中,我们将对这些寻址方式做一个归纳总结,来加深我们的印象。

感谢围观,转发分享请标明出处,谢谢!

汇编学习教程:灵活寻址(四)相关推荐

  1. 汇编学习教程:走进 bp

    引言 此前我们学习了 bp 寄存器,我们知道 bp 的作用是为访问栈空间数据提供方便,其默认绑定的段寄存器就是 SS 段寄存器.在此前的博文中博主提及到,bp 的作用其实不止方便访问栈空间数据这一条, ...

  2. 汇编学习教程:灵活寻址(三)

    引言: 在上篇博文中,我们学习了一个灵活寻址方式:[bx+idata],该方式我们可以形象的将它类比成高级语言中的一维数组,其中idata可以看成是数组的起始地址,bx看成是数组的下标. 除此之外,我 ...

  3. 汇编学习教程:灵活寻址(二)

    引言 在上篇博文中,我们主要学习了两个指令and和or,它们两个目前最主要的功能就是对英文字母进行大小写转换.学习这两个指令主要是为接下来的学习做铺垫,因为接下来的编程案例中需要使用到. 那么从本片开 ...

  4. 汇编学习教程:定义不同的段

    引言 截至目前为止,我们所编写的汇编程序中有且只有一个段,那就是代码段.代码段当然是必不可少的,要不然我们的代码放在哪里呢! 在上篇博文中,我们学习了如何在代码段中申请空间,将申请过来的空间用来当作数 ...

  5. 汇编学习教程:bx的作用

    引言 在上篇博文中,我们已经熟悉了解如何编译.连接并运行一个程序,对MSAM工具和LINK工具的使用也已经掌握.那么从本片博文开始,也就意味着工具类的学习告此段落,下面将会是汇编语言开发方面的讲述. ...

  6. 汇编学习教程:循环和CX寄存器

    引言 在上面博文中,我们主要学习了BX寄存器配合DS寄存器完成内存访问,同时也探究了Masm编译器面对弱指定和强指定两种情况时所产生的不同编译结果. 我们提到:xx:[idata] 是强指定格式,这种 ...

  7. 汇编学习教程:编译、连接、运行

    引言 在上篇博文中,我们详细学习了汇编源程序的组成,并且下载好两个必须的工具:编译源程序的工具 MASM.exe和处理中间文件的工具 LINK.exe. 我们在上篇博文中只是大致了解了一下MASM.e ...

  8. 汇编学习教程:CPU内的小秘密

    引言 在上一篇博文中,我们主要讲述了CPU大致的工作原理,以及CPU的小伙伴存储器,CPU与其他电子器件的交互逻辑等,那么本篇博文将由外及内,回过头来说说CPU内的一些知识点.那么就让我们赶紧开始本次 ...

  9. 汇编学习教程:开发第一步

    引言 此前我们详细讲述了有关内存访问.CPU相关知识.寄存器相关等等,而这些的目的最终都是为了实战开发而准备的.从本片博文开始,我们将正式进入开发相关的学习,相信大家已经急不可耐了,准备一腔热血写代码 ...

最新文章

  1. leetcode 454. 四数相加 II c语言
  2. 特征选择(feature_selection)
  3. 不同编程语言的取模运算%
  4. 华为eudemon 200E的hrp双心跳热备配置
  5. 关于framework4.5的相关介绍
  6. 读《世界是数字的》笔记
  7. Arcgis 重装 的 license 问题
  8. 阶段3 2.Spring_06.Spring的新注解_1 spring的新注解-Configuration和ComponentScan
  9. 幸运的袋子(深度优先遍历(Depth First Search,DFS))
  10. 中国联通企业介绍(联通笔试背景知识)
  11. 给新生的软件网站工具推荐
  12. java 网络爬虫_使用Java实现网络爬虫
  13. 计算机字体对于现代设计有何意义,字体设计课程计算机授课方式的探索.doc
  14. 压力测试工具 ab工具
  15. 国美零售带货直播 重构本地零售数字化生产力
  16. 智能POS删除文件和数据库操作步骤
  17. Ant Design Pro 从零到一教程
  18. linux下postgresql创表添加数据
  19. Day 15 正则表达式
  20. linux备份磁盘数据,linux下vmstat输出数据分析-linux下dd命令备份磁盘的节点(...-linux watch命令用法简介(图文)_169IT.COM...

热门文章

  1. 【论文简述】Learning Inverse Depth Regression for Pixelwise Visibility-AwareMulti-View Stereo (IJCV 2022)
  2. 高新企业成长性指标有哪些计算公式?
  3. 关于安卓APP加固基础的总结
  4. php版本算法,微博短链接算法PHP版本
  5. 分布式事务解决方案之TCC
  6. SourceInsight代码工程
  7. JS跳转链接的几种方式
  8. Rulo扫地机器人app_求一款性价比高的扫地机器人?
  9. c语言输出300 800之间最大的素数,辅助谁一级伤害最高?东皇:300,盾山:800,最强混子输出超1000...
  10. OPPO A73s在哪里开启usb调试模式的简单方法