5.1 [bx]

[bx]是什么

和 [0] 有些类似,[0] 表示内存单元,它的偏移地址是 0。

例如:

mov ax, [0]

内存以字节为单位;ax以字(16bit = 2Byte)为单位;al以字节为单位。所以,mov ax, [0] 解读为把偏移地址为 0 的内存单元处的一个字对应的内容复制到 ax 寄存器中。mov al, [0] 解读为把偏移地址为 0 的内存单元处的一个字节对应的内容复制到 ax 寄存器中,因为 al 寄存器的长度为一个字节。

[bx]和内存单元的描述

  • 我们要完整地描述一个内存单元,需要两个信息:

    1. 内存单元的地址;

    2. 内存单元的的长度(类型)。

    我们用[0]表示一个内存单元时,0表示单元的偏移地址,段地址默认在ds中,单元的长度(类型)可以由具体指令中的其它操作对象(比如说寄存器)指出,如前边的 ax、al。若是ax,则以字单位传输;al,则以字节单位传输。

  • 针对在MASM中的情况,[bx]同样也表示一个内存单元,它的偏移地址在bx中,比如下面的指令:

    mov ax, [bx]

    在MASM情况下,要实现“把偏移地址为 0 对应的内存单元的内容传给 ax 寄存器”,只能这么写:

    mov bx, 0
    mov ax, [bx]

题外话

  • 在MASM中,例如下面一段汇编代码:

    xxx.asm 文件内容为:

    assume cs:codesg
    
    codesg segment
    
    fishcc:
      mov ax, 2000H
      mov ds, ax
      mov al, [0]
      mov bl, [1]
      mov cl, [2]
      mov dl, [3]
    
      mov ax, 4C00H
      int 21H
    
    codesg ends
    
    end fishcc

    注:在 MASM 下折腾汇编语言时,代码 mov bl, [1] 不能解读为把偏移地址为 1 处的内存单元的内容复制到 bl 寄存器,而应该解读为把 1 赋值给 bl 寄存器。

  • 在 Debug 中我们若用 -a 输入汇编指令 mov bl, [1] 则按正常解读。解读为把偏移地址为 1 处的内存单元的内容复制到 bl 寄存器。

也就是说,当在以后缀为 .asm 文件内写入汇编指令时,才特殊对待。其它情况按汇编语法解释理解即可。

5.2 Loop指令

英文单词“loop”有循环的含义,显然这个指令和循环有关。

描述性符号 “()”

为了描述上的简洁,在以后的课程中,我们将使用一个描述性的符号 “()” 来表示一个寄存器或一个内存单元中的内容。也就是说括号的作用是取内容。比如:

  • ax 中的内容为 0010H,我们可以这样来描述:(ax) = 0010H

  • 2000:1000 处的内容为 0010H,我们可以这样来描述:(21000) = 0010H

  • 对于 mov ax,[2] 的功能,我们可以这样来描述:(ax) = ((ds) * 16 + 2)

  • 对于 mov [2],ax 的功能,我们可以这样来描述:((ds) * 16 + 2) = (ax)

  • 对于 add ax,2 的功能,我们可以这样来描述:(ax) = (ax) + 2

  • 对于 add ax,bx 的功能,我们可以这样来描述:(ax) = (ax) + (bx)

  • 对于 push ax 的功能,我们可以这样来描述(push,入栈,先修改栈顶指针然后存入数据;pull,出栈,先取出数据后修改栈顶指针。):

    (sp) = (sp) - 2

    ((ss) * 16 + (sp)) = (ax)

  • 对于 pop ax 的功能,我们可以这样来描述:

    (ax) = ((ss) * 16 + (sp))

    (sp) = (sp) + 2

约定符号 idata 表示常量

我们在 Debug 中写过类似的指令:mov ax,[0] 表示将 ds:0 处的数据送入 ax 中。指令中,在 “[...]” 里用一个常量 0 表示内存单元的偏移地址。以后,我们用 idata 表示常量。例如:

  • mov ax,[idata] 就代表 mov ax,[1]mov ax,[2]mov ax,[3] 等。
  • mov bx,idata 就代表 mov bx,1mov bx,2mov bx,3 等。
  • mov ds,idata 就代表 mov ds,1mov ds,2 等,它们都是非法指令(因为段寄存器不能直接操作)。

5.1 [bx]

我们来看一看下面指令的功能:

  • mov ax,[bx]

    功能:bx 中存放的数据作为一个偏移地址 EA,段地址 SA 默认在 ds 中,将 SA:EA 处的数据送入 ax 中。

    即:(ax) = ((ds) * 16 + (bx))

  • mov [bx],ax

    功能:bx 中存放的数据作为一个偏移地址 EA,段地址 SA 默认在 ds 中,将 ax 中的数据送入内存 SA:EA 处。

    即:((ds) * 16 + (bx)) = (ax)

问题 5.1

准备工作:

我们首先在 masm 根目录下创建 test.asm 文件,并写入内容:

assume cs:codesg

codesg segment

fishcc:
        mov ax, 2000H
        mov ds, ax
        mov bx, 1000H
        mov ax, [bx]
        inc bx
        inc bx
        mov [bx], ax
        inc bx
        inc bx
        mov [bx], ax
        inc bx
        mov [bx], al
        inc bx
        mov [bx], al

codesg ends

end fishcc

然后 Debug 生成的 test.exe 这个程序,单步调试来看一看。以上过程如下图:

然后修改内存 2000:1000 处的内容为 be 00

查看是否修改成功:

问题 5.1 分析

  1. 先看一下程序的前三条指令:

    mov ax, 2000H
    mov ds, ax
    mov bx, 1000H

    这三条指令执行后:

    ds = 2000H

    bx = 1000H

    附图:

  2. 再看第4条指令:mov ax, [bx] 。执行前:ds = 2000H, bx = 1000H,则 mov ax, [bx] 将把内存 2000:1000 处的字型数据送入 ax 中。该指令执行后,ax = 00beH。

    附图:

  3. 再看5、6条指令:

    inc bx
    inc bx

    指令执行前:bx = 1000H

    执行后:bx = 1002H

    附图:

  4. 再看第7条指令:mov [bx], ax

    指令执行前:ds = 2000H, bx = 1002H,则 mov [bx], ax 将把 ax 中的数据送入内存 2000:1002 处。

    指令执行后:2000:1002 单元的内容为 BE,2000:1003 单元的内容为 00。

    附图:

  5. 接下来,第8、9条指令:

    inc bx
    inc bx

    这两条指令执行前 bx = 1002H,执行后 bx = 1004H。

    附图:

  6. 接下来,第10条指令:mov [bx], ax

    指令执行前:ds = 2000H,bx = 1004H,则 mov [bx], ax 将把 ax 中的数据送入内存 2000:1004 处。

    指令执行后,2000:1004 单元的内容为 BE,2000:1005 单元的内容为 00。

  7. 接下来,第11条指令:inc bx。这条指令执行前 bx = 1004H,执行后 bx = 1005H。

    附图:

  8. 接下来,第12条指令:mov [bx], al

    指令执行前:ds = 2000H,bx = 1005H,则 mov [bx], al 将把 al 中的数据送入内存 2000:1005 处。指令执行后,2000:1005 单元的内容为 BE。

    附图:

  9. 接下来,第13条指令:inc bx。这条指令执行前 bx = 1005H,执行后 bx = 1006H。

    附图:

  10. 接下来,第14条指令:mov [bx], al

指令执行前:ds = 2000H,bx = 1006H,则 mov [bx], al 将把 al 中的数据送入内存 2000:1006 处。指令执行后,2000:1006 单元的内容为 BE。

附图:

### Loop 指令

  • 格式:loop 标号
  • CPU执行loop指令的时候,要进行两步操作:
    1. (cx) = (cx) - 1;
    2. 判断 cx 中的值,不为零则转至标号处执行程序,如果为零则向下执行。
  • 从上面的描述中,我们可以看到,cx 中的值影响着loop指令的执行结果。通常(注意,我们说的是通常情况)我们用loop指令来实现循环功能,cx 中存放循环次数。下面我们结合一个例子来具体看一看loop指令执行流程。

### 程序 5.1

在MASM环境的根目录下创建待编译文件loop.asm,在其中写入内容:

assume cs:code
code segment

addrs:
 mov ax,2
 mov cx,11
  s:add ax, ax
 loop s

 mov ax,4c00h
 int 21h

code ends
end addrs

注意:微软的MASM默认是十进制的,所以指令 mov ax,4c00h 中的 4c00 要加上 h/H 。Debug默认是十六进制的,所以我们在debug中则用加 h/H

然后 Debug 生成的 loop.exe 这个程序,单步调试来看一看。以上过程如下图:

首先我们把ax寄存器的值赋值为2,cx原本默认是存储这个程序的大小,在这里表面程序的大小为000FH,即15个字节。

附图:

然后指令 mov cx,000b 将 11 赋值给 cx 寄存器。

附图:

观察 cx 寄存器的值,直到 cx 减为 0。此时循环结束,ax 寄存器的值为 1000H,转换成十进制正好是 4096 = \(2^{12}\) 。

附图:

然后用debug的 t 命令继续执行循环后边的语句 mov ax,4c00hint 21 表示程序结束,键入debug的 p 命令结束程序;接着键入debug的q命令退出Debug。键入 exit 退出终端。

附图:

小结

从上边的课程中,我们可以总结出用 cx 和 loop 指令相配合实现循环功能的三个要点:

  1. 在 cx 中存放循环次数;
  2. loop指令中的标号所标识地址要在前面;
  3. 循环执行的程序段,要写在标号和loop指令的中间。

用 cx 和 loop 指令相配合实现循环功能的程序框架如下:

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

指令 loop s 会做三件事:

  1. (cx) = (cx) - 1;
  2. 判断 cx 中的值,不为零则转至标号(s)处执行程序;
  3. 如果为零则向下执行。

温故而知新

  • [bx] 的作用:作为偏移地址与DS配合
  • loop 和 cx 合作
  • debug 的 -g 偏移地址命令和 -p 命令

5.3 在Debug中跟踪用loop指令实现的循环程序

5.4 Debug和汇编编译器Masm对指令的不同处理

正是由于它俩对汇编语法的不同处理,诞生了 [bx] 的应用。

我们在Debug中写过类似的指令:

mov ax,[0]

表示将 ds:0 处的数据送入 al 中。因为内存单元是一个字节,ax 是两个字节。所以,该语句使 al 内容为 ds:0 处的数据;ah 默认置为 0。

但是在汇编元程序中(即:Masm),指令“mov ax,[0]”被编译器当作指令“mov ax,0”处理。

示例:

将内存2000:0、2000:1、2000:2、2000:3单元中的数据送入al、bl、cl、dl中。

  1. 在Debug中编程实现
  2. 汇编程序实现

两种实现的实际实施情况

在Debug中编程实现

回车

debug的 -r 命令:查看当前寄存器的值;

debug的 -t 命令:向前执行一条指令;

debug的 -d 命令:查看内存单元内容(比如查看内存从地址1000:0处开始,8个字节的内容);

debug的 -e 命令:修改内存单元内容(比如修改内存地址1000:0单元的内容);

debug的 -u 命令:将内存中的机器码翻译为汇编代码;

debug的 -a 命令:以汇编语言的形式在内存中写入一段程序。

我们现在用 -r 命令查看当前 ds 寄存器内容(即当前地址),在此处写入一段汇编代码:

接着设置 CS:IP 的值为 0b2a:0000 ,CS 的值已经是 0b2a,只要把 IP 指向 0000 即可。

附图:

注:在debug中默认的单位是十六进制;在Masm中默认的单位是十进制。

接着就可以用debug的 -t 命令单步跟踪观察刚刚输入的汇编代码对寄存器操作的情况了。

附图:

对比汇编程序实现

在Masm环境的根目录创建测试文件 test.asm ,写入下面代码:

assume cs:code
code segment
    mov ax,2000h
    mov ds,ax
    mov al,[0]
    mov bl,[1]
    mov cl,[2]
    mov dl,[3]

    mov ax,4c00h
    int 21h
code ends
end

注意

  • Masm只认十进制,所以第二行的 h/H 不要忘记写。
  • 还有就是 Masm 不认中括号,类似于 mov al,[0] 这样的命令,实现的效果是把 0 赋值给 al 寄存器,而不是把内存偏移地址 0 处的内容复制到 al 寄存器。 mov al,[0]mov al,0 在 Masm 是等价的。

附图:

注:如上边注意中提到一样。我们在 test.asm 中写的汇编命令是 mov al,[0] ,Masm 只会把 0 赋值给 al 寄存器。

mov bl,[1]
mov cl,[2]
mov dl,[3]

也是一样的道理:

若我们想把内存偏移地址 0 处的内容复制到 al 寄存器,怎么实现呢?这时候需要借助 bx 寄存器。

修改上边 test.asm 中的汇编代码如下:

assume cs:code
code segment
    mov ax,2000h
    mov ds,ax
    mov bx,0
    mov al,[bx]
    mov bx,1
    mov bl,[bx]
    mov bx,2
    mov cl,[bx]
    mov bx,3
    mov dl,[bx]

    mov ax,4c00h
    int 21h
code ends
end

重新编译 test.asm ,调试 test.exe。即可实现我们想要的把内存偏移地址 0 处的内容复制到 al 寄存器。因为内存地址 2000:0000 单元的内容此例正好为0,我们不好看出效果。我们直接看把内存偏移地址 1 处的内容复制到 bl 寄存器的情况。

附图:

注:内存单元 2000:0 和 2000:1 的内容都为0。

如果一定要像DEBUG那样

在MASM中 mov ax,[2] 是解释为 mov ax,2 的。一般我们是通过 bx 来代替,像前边我们先 mov bx,2 再通过 mov ax,[bx] 来实现。但是我们要像DEBUG一样直接用 [2] 可以吗?答案是可以的,不过要加上段地址。

assume cs:code
code segment
    mov ax,2000h
    mov ds,ax
    mov al,ds:[0]
    mov bl,ds:[1]
    mov cl,ds:[2]
    mov dl,ds:[3]

    mov ax,4c00h
    int 21h
code ends
end

5.5 loop和[bx]的联合应用

5.6 段前缀

  • 指令 mov ax,[bx] 中,内存单元的偏移地址由 bx 给出,而段地址默认在 ds 中。

5.7 一段安全的空间

在一般的PC机中,DOS方式下(实模式下),DOS和其他合法的程序一般都不会使用 0:200~0:2FF (0:200h~0:2FFh) 的256个字节的空间。所以,我们使用这段空间是安全的。

5.8 段前缀的使用

第五章 [BX]和loop指令的更多相关文章

  1. [汇编学习笔记][第五章[BX]和loop指令]

    第五章[BX]和loop指令 前言 定义描述性符号“()”来表示一个寄存器或一个内存单元的内容,比如: (ax)表示ax中的内容,(al)表示al的内容. 约定符号ideta表示常量. 5.1 [BX ...

  2. 王爽汇编第五章,[bx]和loop指令

    目录 王爽汇编第五章,[bx]和loop指令 [bx]和loop指令 例子: 王爽汇编第五章,[bx]和loop指令 [bx]和loop指令 [bx]之前我们介绍寄存器的时候,已经很详细的说明过了,b ...

  3. [汇编语言]-第五章[bx]和loop指令

    1- [bx]和内存单元的描述 [0]表示内存单元, 他的偏移地址为0 mov ax,[0] 将一个内存单元的内容送入到ax.这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在d ...

  4. 汇编语言-[BX]和loop指令

    汇编语言-[BX]和loop指令 [BX]指令介绍 mov ax,[bx] 功能: bx为偏移地址,段地址默认为ds.因此,上面指令作用就是将ax中的数据送入内存ds:bx处,即:((ds)*16 + ...

  5. 80806汇编(5)——[BX]和Loop指令

    80806汇编(5)--[BX]和Loop指令 已经好久没写点东西了,国庆节就一直想弄个个人网站,这段时间一直在弄那个,虽然有现成的框架(Hexo),但是总想弄出自己的效果来,但是最后还是有些差强人意 ...

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

         这一章主要介绍什么是[BX]以及loop(循环)指令怎么使用,loop和[BX]又怎么样相结合,段前缀又是什么鬼,以及如何使用段前缀.   1.[BX]的概念      [BX]和[0]类似 ...

  7. [bx]和loop指令

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  8. 汇编语言-[bx]和loop指令和多个段

    5.1 [BX]和内存单元的描述 要完成描述一个内存单元,需要两种信息: 内存单元的地址: 可以用 [0] 表示一个内存单元, 0 表示单元的偏移地址,段地址默认在 ds 中: 同样也可以用 [bx] ...

  9. 实验4 [BX]和loop指令

    实验内容: 1.综合使用loop,[bx],编写完整汇编程序,实现向内存b800:07b8开始的连续16个字单元重复填充字数据0441H. 实验结果: 若填充的数据为:0403h,则实验结果转变为: ...

随机推荐

  1. CTC(Connectionist Temporal Classification)介绍

    CTC解决什么问题 CTC,Connectionist Temporal Classification,用来解决输入序列和输出序列难以一一对应的问题. 举例来说,在语音识别中,我们希望音频中的音素和翻 ...

  2. Python中*args和**kwargs的区别

    (注:本文部分内容摘自互联网,由于作者水平有限,不足之处,还望留言指正.) 中秋的夜,微凉,但却始终看不见月亮. 我想,它一定是害羞了,悄悄的躲到了乌云的后面. 嗯,就是这样,我真是太TM机智了. 正 ...

  3. 打造高可靠与高性能的React同构解决方案

    前言 随着React的兴起, 结合Node直出的性能优势和React的组件化,React同构已然成为趋势之一.享受技术福利的同时,直面技术挑战,在复杂场景下,挑战10倍以上极致的性能优化. 什么是同构 ...

  4. php 获取随机数的几个方式

    php 获取随机数的几个方式 1.直接获取从min-max的数,例如1-20:$randnum = mt_rand(1, 20); 2.在一个数组里面随机选择一个(验证码的时候需要字母.数字混合的情况 ...

  5. selinux配置错误实例介绍

    错误原因 配置关闭SELinux,结果误操作 应修改配置文件/etc/selinux/config中的“SELINUX”参数的值, # SELINUX=enforcing  原始配置 SELINUX= ...

  6. 0-5v转0-20ma和0-5v转4-20ma

    0-5v转0-20ma 0-5v转4-20ma

  7. 学写网页 #05# CSS Mastery 笔记 1~3

    看到第四章才发现这本书已经太旧了..看到第 3 章为止吧.前三章主要讲的内容:一些编码常识.怎样选择元素.盒子模型(主要是 Margin).定位(绝对.相对.浮动.fixed 等) 第一章 conve ...

  8. Core Java 2

    p267~p270: 1.一个方法不仅需要告诉编译器将要返回什么值, 还要告诉编译器有可能发生什么错误(以便在错误发生时用妥善的方式处理错误). 2.方法应该在首部声明所有可能抛出的异常. 3.方法抛 ...

  9. 20145307陈俊达_安卓逆向分析_Xposed的hook技术研究

    20145307陈俊达_安卓逆向分析_Xposed的hook技术研究 引言 其实这份我早就想写了,xposed这个东西我在安卓SDK 4.4.4的时候就在玩了,root后安装架构,起初是为了实现一些屌 ...

  10. 小K(wifi)插座剖解

    1.主控 AR9331 400MHZ MIPS 24k内核 2.flash:w9425G6JH-5 1352P 6316CF500ZY  RAM 32M