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

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

已经好久没写点东西了,国庆节就一直想弄个个人网站,这段时间一直在弄那个,虽然有现成的框架(Hexo),但是总想弄出自己的效果来,但是最后还是有些差强人意,只好打翻了有重来(强迫症表示难受),也懒得弄那么多了。好在最近差不多事情也理顺了,今天强迫自己静下来写了点东西。


简述

今天看了下汇编中的[BX]和Loop指令,Loop指令容易知道,一看就是用来做循环的指令,那么[BX]又是什么呢?

首先我们知道,要完整的描述一个内存单元,需要两种信息:

  • 内存单元的地址
  • 内存单元的长度(类型:字节或字等)

对于第一个——【内存单元的地址】,我们知道,用[addr]可以表示一个内存单元,其中addr表示这个内存单元的偏移地址,段地址默认在DS寄存器中。而单元的长度(类型)则可以由指令中的其他操作对象指出(如寄存器),就像下面这样:

mov ax, [0]

那么,在这里[BX]也表示一个内存单元,其偏移地址存于BX寄存器中,段地址默认在DS寄存器中。也把BX寄存器叫做基址寄存器

两个约定

为了方便后面的叙述,这里作两个约定,当然也是跟着书上的来啦,哈哈,不过我发现很多书上也都默认有这个叙述方法。

1. 描述性符号"( )"

用描述性符号“( )”表示一个寄存器或一个内存单元中的内容,如:

(ax) 表示寄存器AX中的内容,(al) 表示寄存器AL中的内容。
(addr) 表示物理地址为addr的内存单元中的内容。

因此"( )"中的元素类型有3种:

  1. 寄存器名;
  2. 段寄存器名;
  3. 内存单元的物理地址。

其所表示的数据类型有:

  1. 字节;
  2. 字。

具体是那种类型则由寄存器名或具体的运算符决定。

2. 常量符号"idata"

用idata表示一个常量。
如用mov ax, [idata]代表mov ax, [1]mov ax, [2]等。
如用mov bx, idata代表mov bx, 1mov bx, 2等。

[BX]

我们对[BX]的使用有如下用法:

mov ax, [bx]  ; bx中存放的数据作为偏移地址,段地址默认在ds中,(ax) = ((ds) * 16 + (bx))
mov [bx], ax  ; bx中存放的数据作为偏移地址,段地址默认在ds中,((ds) * 16 + (bx)) = (ax)

Loop指令

Loop指令用于实现循环功能,循环次数存于CX寄存器中 。
【格式】loop 标号
示例:

mov cx, 5      ; 设置循环次数
s:                  ; 标号
add ax, ax     ; 循环执行的程序段
loop s           ; Loop指令

Loop指令通常用于执行某些需要重复运行的指令,比如操作某一块连续的内存地址块。通常把循环次数存于CX寄存器中,CPU通过判断CX里面的值是否为零来决定是否执行循环。CPU执行Loop指令的描述如下:

1. (CX) = (CX) -1
2. (CX)不为零,转去标号处执行,反之不执行循环,向下执行指令

这里的标号"s"并不是固定的,你也可以指定其他的标号。在这里标号实际上是标识了一个地址,在该地址处存在着我们的程序指令(如上面的add ax, ax),在执行循环的时候,若CX的值不为零,则"Loop 标号"指令将IP的值设置为标号所标识的地址,CPU就将执行IP所指向的指令。

我们可以得到这样一个Loop指令的简单框架:

mov cx, 循环次数
label:
需要循环执行的程序段
loop label

【注意】书上的一个实例程序中提到在汇编程序中的数据表示问题,我们知道,在汇编中,数据多以16进制表示,而16进制中有 'A~F' 6个字母,但是在汇编源程序中,数据的书写不能以字母开头,如“9876H”可以直接书写为“9876H”,但是“A000H”不能书写成“A000H”,而需写为“0A000H”(以0开头)。

[BX]和Loop指令

在实际编程中可能会遇到要处理某一段地址连续的内存单元中的数据,通常可用循环来解决这类问题。在这样的循环过程中,需要一个变量来保存内存单元的偏移地址,这时候“基址寄存器——BX”就起作用了。

【注:以下内容来自王爽的《汇编语言》(第三版)】
考虑这么一个问题,我们要计算 ffff:0~ffff:b 单元中的数据的和,要求将结果存储在dx寄存器中。
我们首先分析一下:

  1. 运算后的结果是否会超出dx能存储的范围?
    ffff:0~ffff:b 内存单元中的数据是字节型的数据,范围在0~255之间,12个这样的数据相加的结果不会大于65535,是可以存储在dx寄存器中的。
  2. 我们能否将 ffff:0~ffff:b 中的数据直接累加到dx中?
    这是比不可能的,为什么?因为数据类型不匹配,dx是16位寄存器,而 ffff:0~ffff:b 中的数据是8位的。
  3. 那么是否可以将其累加到dl中,并设置(dh)=0,从而实现累加到dx中?
    看第一条分析,12个8字节的数据相加是有可能超过255的,而我们的dl是8位寄存器,那么答案很明显了。不能!
  4. 那到底应该如何做呢?
    这里有两个问题,类型的匹配和结果的不超界,我们可以考虑用一个16位的寄存器来做中介。将内存单元中的8位数据赋值到一个16位寄存器ax中,再将ax中的数据加到dx上,从而使两个运算对象类型匹配并且结果不会超界。

考虑完以上问题,很明显,如果是在高级语言中,这是一个很容易做的问题,一个循环就可以实现,那么在汇编中,我们也可以用循环来实现这个操作。我们可以把BX用作存储内存单元偏移地址的变量,在每次循环的时候改变BX中的值,从而实现把 ffff:0~ffff:b 12个内存单元中的数据相加。
实现如下:

assume cs:code
code segment
    start:             ;程序入口
    mov ax, 0ffffh
    mov ds, ax
    mov bx, 0       ;初始化ds:bx指向ffff:0

    mov dx, 0      ;初始化累加寄存器dx,(dx)=0

    mov cx, 12
    s:
    mov al, [bx]
    mov ah, 0
    add dx, ax
    inc bx          ;使ds:bx指向下一个内存单元
    loop s

    mov ax, 4c00h
    int 21h
code ends
end start        ;程序结束

段前缀

在指令“mov ax, [bx]”中,内存单元的偏移地址由bx指出,而段地址则默认存储在ds中。但是我们可以在访问内存单元的指令中显示给出内存单元的段地址所在的段寄存器,如:

mov ax, ds:[bx]
mov ax, cs:[bx]
mov ax, ss:[bx]
mov ax, es:[bx]
mov ax, ss:[0]
mov ax, cs:[0]

这些出现在访问内存单元的指令中,用于显示地指明内存单元的段地址的“ds:” “cs:” “es:” “ss:”,在汇编语言中称为段前缀

段前缀的使用

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

  1. 0:200 ~0:20b 单元等同于 0020:0~0020:b 单元,它们描述的是同一段内存空间。
  2. 复制过程用循环实现,我们可以使用一个变量[BX]同时操作两段内存单元(使用段前缀来标识不同的段)。
  3. 将 0:200~0:20b 用 0020:0~0020:b 描述,是为了使目标单元的偏移地址和源始单元的偏移地址从同一数值0开始。

实现如下:

assume cs:code
code segment
    mov ax, 0ffffh
    mov ds, ax           ;(ds)=0ffffh

    mov ax, 0020h
    mov es, ax         ;(es)=0020h

    mov bx, 0   ;此时ds:bx指向ffff:0,es:dx指向0020:0

    mov cx, 12
    s:
    mov dl, [bx]      ;段地址默认在ds中
    mov es:[bx], dl   ;段前缀指明内存段
    inc bx
    loop s

    mov ax, 4c00h
    int 21h
code ends
end

转载于:https://www.cnblogs.com/exRunner/p/7719894.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值