之前用[0][bx]的方法访问内存的指令中,定位内存单元的地址。本章主要通过具体的问题来讲解一些更灵活的定位内存地址的方法和相关的编程方法

一、and和or指令

先介绍一下andor指令

1.1 and指令

and指令:逻辑与指令,按位进行与运算,例如指令

  1. mov al,01100011B
  2. and al,00111011B

执行后,al = 00100011B

通过该指令可将操作对象的相应位设为0,其他保持不变,例如

  1. and al,10111111B ; al的第6位设为0的指令
  2. and al,01111111B ; al的第7位设为0的指令
  3. and al,11111110B ; al的第0位设为0的指令

1.2 or指令

or指令:逻辑或指令,按位进行或运算,例如指令

  1. mov al,01100011B
  2. or al,00111011B

执行后,al=01111011B

通过该指令可将操作对象的相应位设为1,其他位不变,例如

  1. or al,01000000B ; al的第6位设为1
  2. or al,10000000B ; al的第7位设为1
  3. or al,00000001B ; al的第0位设为1

二、关于ASCII码

计算机中,所有的信息都是二进制,而人能理解的信息都是已经具有约定意义的字符。比如说,人在有一定上下文得情况下看到123,就可以知道这是一个数值,他的大小为123;看到BASIC就知道这是在说BASIC这种编程语言;看到desk就知道说的是桌子。

而我们需要把这些信息存储在计算机中,就要对其进行编码,将其转化为二进制信息进行存储。而计算机要将这些存储的信息再展示给我们看,就要再对其进行解码。只要编码和解码采用相同的规则,我们就可以将人能理解的信息存入到计算机,再从计算机中取出。

世界上有很多编码方式,有一种方案叫做ASCII编码,实在计算机系统中通常被采用的。简单地来说,所谓编码方案,就是用一套规则,它约定了用什么样的信息来表示现实对象。比如说,再ASCII编码方案中,用61H表示a,62H表示b,一种规则需要人们遵守才有意义。

一个文本编辑过程中,就包含着按照ASCII编码规则进行的编码和解码。在文本编辑过程中,我们按一下键盘的a键,就会在屏幕上看到a,这是一个怎么样的过程呢?

我们按下键盘的a键,这个按键的信息就会被送入计算机,计算机用ASCII码的规则对其进行编码,将其转化为61H存储在内存的指定空间中;文本编辑软件从内存中取出61H,将其送到显卡上的显存中;工作在文本模式下的显卡,用ASCII码的规则解释现存中的内容,61H被当作字符”a”,显卡驱动显示器,将字符”a”的图像画在屏幕上。我们可以看到,显卡在处理文本信息的时候,是根据ASCII码的规则进行的。这也就是说,如果我们要想在显示器上看到”a”,就要给显卡提供”a”的ASCII 码,61H。如何提供?当然是写入显存中

§ 第7章 更灵活的定位内存地址的方法 - 图1

三、以字符形式给出的数据

我们可以在汇编程序中,用' '的方式指明数据是以字符的形式给出的,编译器将把它们转化为想对印的ASCII码,如下面的程序

  1. ; *****程序7.1*****
  2. assume cs:code,ds:data
  3. data segment
  4. db 'unIX'
  5. db 'foRK'
  6. data ends
  7. code segment
  8. start: mov al,'a'
  9. mov bl,'b'
  10. mov ax,4c00h
  11. int 21h
  12. code ends
  13. end start

上面的源程序中:

  • db unIX相当于db 75H,6EH,49H,58Hu, n, I, X的ASCII码分别为75H, 6EH, 49H和58H
  • db foRK相当于db 66H,6FH,52H,4BHf, o, R, K的ASCII码分别为66H, 6FH, 52H和4BH
  • mov al,'a'相当于mov al,61H'a'的ASCII码为61H
  • mov bl,'b'相当于mov bl,62H'b'的ASCII码为62H

将上述程序编译、连接后,Debug的r命令查看

§ 第7章 更灵活的定位内存地址的方法 - 图2

因为DS=075A,data段又是程序中的第一个段,它就在程序的起始处,所以它的段地址为076AH,现在使用d查看其中的内容,可以看到其中存放的数据段的内容。

§ 第7章 更灵活的定位内存地址的方法 - 图3

还记得之前这张图吗?

§ 第7章 更灵活的定位内存地址的方法 - 图4

这张图就说明了 § 第7章 更灵活的定位内存地址的方法 - 图5,当然这是在没有给ds赋值之前的,如果使用了mov ds,data,就相当于把data段的段地址给了DS,此时DS就指向了不同的内容

四、大小写转换的问题

下面考虑这样一个问题,在codesg中填写代码,将datasg中的第一个字符串转化为大写,将第二个字符串转化为小写

  1. assume cs:codesg,ds:datasg
  2. datasg segment
  3. db 'BaSiC'
  4. db 'iNfOrMaTiOn'
  5. datasg ends
  6. codesg segment
  7. start:
  8. codesg ends
  9. end start

首先分析一下,我们知道同一个字母的大写字符和小写字符对应的ASCII码是不同的,比如”A”的ASCII码是41H,”a”的ASII码是61H。要改变一个字母的大小写,实际上就是要改变它对应的ASII码。我们可以将所有的字母的大写字符和小写字符对应的ASCII码列出来,进行对比,从其中找到规律。

大写字母:及其对应的十六进制:对应的二进制 小写字母:及其对应的十六进制:对应的二进制
A:41H:01000001 a:61H:01100001
B:42H:01000010 b:62H:01100010
C:43H:01000011 c:63H:01100011
D:44H:01000101 d:64H:01100100
E:45H:01000101 e:65H:01100101
F:46H:01000110 f:66H:01100110

从中我们得到如下规律

§ 第7章 更灵活的定位内存地址的方法 - 图6

按照上面的规律,实现大小写转换

  1. assume cs:codesg,ds:datasg
  2. datasg segment
  3. db 'BaSiC'
  4. db 'iNfOrMaTion'
  5. datasg ends
  6. codesg segment
  7. start:mov ax,datasg
  8. mov ds,ax
  9. mov bx,0
  10. mov cx,5
  11. s:mov al,[bx]
  12. ; 如果(al)>61H,则为小写字母的ASCII码,则sub al,20H
  13. mov [bx],al
  14. inc bx
  15. loop s
  16. ...
  17. code ends
  18. end start

但是,判断指令我们还没有学到,所以不能解决如果al>6H这个要求,现在应该怎么办呢?

如果一个问题的解决方案,是我们陷入了一种矛盾之中。那么,很可能是我们考虑的出发点有了问题,或是说,我们起初运用的规律并不合适。

§ 第7章 更灵活的定位内存地址的方法 - 图7

这个时候应该重新观察,寻找新的规律,观察其二进制数据,可以看到大写字母的第5位数字为0,而小写字母的第5位数字为1,这个就是新的规律了。

§ 第7章 更灵活的定位内存地址的方法 - 图8

用什么方法将一个数据中的某一个位 置0还是置1?就是用刚刚学习过的orand指令,完整程序如下

  1. assume cs:codesg,ds:datasg
  2. datasg segment
  3. db 'BaSiC'
  4. db 'iNfOrMaTion'
  5. datasg ends
  6. codesg segment
  7. start:mov ax,datasg
  8. mov ds,ax ; 设置ds指向datasg
  9. mov bx,0 ; 设置 (bx)=0, ds:bx指向'BaSiC'的第一个字母
  10. mov cx,5 ; 设置循环次数为5
  11. s:mov al,[bx] ; ASCII码从ds:bx所指向的单元中取出
  12. and al,11011111B ; al中的ASCII码的第5 0,变为大写字母
  13. mov [bx],al ; 将转变后的ASCII码写回原单元
  14. inc bx ; (bx)加1ds:bx指向下一个字母
  15. loop s
  16. mov bx,5 ; 设置(bx)=5ds:bx指向'iNfOrMaTiOn'的第一个字母
  17. mov cx,11 ; 设置循环次数11,因为'iNfOrMaTiOn'11个字母
  18. s0:mov al,[bx]
  19. or al,00100000B ; al中的ASCII码的第5位设为1,变为小写字母
  20. mov [bx],al
  21. inc bx
  22. loop s0
  23. mov ax,4c00h
  24. int 21h
  25. code ends
  26. end start

五、[bx+idata]

指明一个内存单元的方式

  • [bx]的方式来指明一个内存单元
  • [bx+idata]来表明一个内存单元,它的编译地址为§ 第7章 更灵活的定位内存地址的方法 - 图9%2Bidata%7D#card=math&code=%5Ctext%7B%28bx%29%2Bidata%7D&id=EufF3)(§ 第7章 更灵活的定位内存地址的方法 - 图10中的数值加上§ 第7章 更灵活的定位内存地址的方法 - 图11

例如下面的指令

  1. ; 下面三种写法意思一样
  2. mov ax,[bx+200]
  3. mov ax,[200+bx]
  4. mov ax,[bx].200
  5. ; 将一个内存单元的内容送入ax,这个内存单元的长度为2个字节(字单元),存放一个字,
  6. ; 偏移地址为bx中的数值加上200,段地址在ds

六、用[bx+idata]的方式进行数组的处理

有了§ 第7章 更灵活的定位内存地址的方法 - 图12这种内存表示的方式,我们就可以用更高级的结构来看待所要处理的数据。通过下面的问题来理解这一点。

在codesg中填写代码,将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写。

  1. ; 一个大致的框架程序
  2. assume cs:codesg,ds:datasg
  3. datasg segment
  4. db 'BaSiC'
  5. db 'MinIX'
  6. datasg ends
  7. codesg segment
  8. start:
  9. ; 填充的代码
  10. codesg ends
  11. end start

有了上述的框架后,现在就需要往其中填写代码实现所需要的功能,现在使用原来的[bx]的方式来定位字符位置,代码如下

  1. ; 使用[bx]方式来决定字符的位置
  2. mov ax,datasg
  3. mov ds,ax
  4. mov bx,0
  5. mov cx,5
  6. s: mov al,[bx]
  7. and al,11011111b
  8. mov [bx],al
  9. inc bx
  10. loop s
  11. mov bx,5
  12. mov cx,5
  13. s0: mov al,[bx]
  14. or al,000100000b
  15. mov [bx],al
  16. inc bx
  17. loop s0

现在,我们有了[bx+idata]的方式,就可以用更简化的方法来完成上面的程序,对上面的程序进行改进过后的代码如下

  1. ; 改进版本1:使用[bx+idata]方式完成上述功能
  2. mov ax,datasg
  3. mov ds,ax
  4. mov bx,0
  5. mov cx,5
  6. s: mov al,[bx] ; 定位第一个字符串中的字符
  7. and al,11011111b
  8. mov [bx],al
  9. mov al,[5+bx] ; 定位第二个字符串中的字符
  10. or al,00100000b
  11. mov [5+bx],al
  12. inc bx
  13. loop s

程序也可以写为下面这个样子

  1. ; 改进版本2:使用idata[bx]方式完成上述功能
  2. mov ax,datasg
  3. mov ds,ax
  4. mov bx,0
  5. mov cx,5
  6. s: mov al,0[bx] ; 定位第一个字符串中的字符
  7. and al,11011111b
  8. mov [bx],al
  9. mov al,5[bx] ; 定位第二个字符串中的字符
  10. or al,00100000b
  11. mov 5[bx],al
  12. inc bx
  13. loop s

如果使用高级语言C语言来描述上面的程序,大致是这样的

  1. char a[5]="BaSiC";
  2. char b[5]="MinIX";
  3. main ()
  4. {
  5. int i;
  6. i = 0;
  7. do
  8. {
  9. a[i] = a[i] & 0xDF;
  10. b[i] = b[i] | 0x20;
  11. i++;
  12. }
  13. while (i<5);
  14. }

可以比较一下这个C程序和上面的汇编程序的相似之处,尤其注意它们定位字符串中字符的方式。

  1. C语言: a[i], b[i]
  2. 汇编语言: 0[bx], 5[bx]
  3. 通过比较,可以发现,[bx+idata]的方式为高级语言实现数组提供了便利机制

七、SI和DI

SI和DI是8086CPU中和bx功能相近的寄存器,SI和DI不能够被分为两个8位寄存器来使用,下面的3组指令实现了相同的功能。

  1. ; 下面的3组指令实现相同的功能
  2. mov bx,0
  3. mov ax,[bx]
  4. mov si,0
  5. mov ax,[si]
  6. mov di,0
  7. mov ax,[di]

下面的3组指令也实现了相同的功能

  1. mov bx,0
  2. mov ax,[bx+123]
  3. mov si,0
  4. mov ax,[si+123]
  5. mov di,0
  6. mov ax,[di+123]

八、[bx+si]和[bx+di]

在前面,我们用[bx(si或di)][bx(si或di)+idata]的方式来指明一个内存单元,我们还可以用更为灵活的方式:[bx+si][bx+di]

[bx+si][bx+di]的含义相似,以[bx+si]为例子进行讲解

  1. ; [bx+si]表示一个内存单元,偏移地址为(bx)+(si),即bx中的数值加上si中的数值
  2. mov ax,[bx+si]
  3. ; 指令的意思:将一个内存单元中的内容送入ax,这个内存单元的长度为2字节,存放一个字
  4. ; 偏移地址为bx中的数值加上si中的数值
  5. ; 段地址在ds

之前用的[bx+idata]idata[bx]的意思一致,这里同样的

  1. ; 下面3条指令意义一致
  2. mov ax,[bx+si]
  3. mov ax,[si][bx]
  4. mov ax,[bx][si]

九、[bx+si+idata]和[bx+di+idata]

之前学习的几种表示方法可以组合起来了,[bx+si+idata][bx+di+idata]的含义相似,以[bx+si+idata]为例进行讲解

  1. ; [bx+si+idata] 表示一个内存单元
  2. ; 偏移地址为(bx)+(si)+idata

以一个指令为例子

  1. mov ax,[bx+si+idata]
  2. ; 将一个内存单元的内容送入ax,这个内存单元的长度为2字节,存放一个字
  3. ; 偏移地址为bx中的数值加上si中的数值再加上idata,段地址在ds

和之前的几种表示方法一样,一下几种指令都代表一个意思

  1. mov ax,[bx+200+si]
  2. mov ax,[200+bx+si]
  3. mov ax,200[bx][si]
  4. mov ax,[bx].200[si]
  5. mov ax,[bx][si].200

十、不同的寻址方式的灵活应用

如果我们比较前面用到的几种定位内存地址的方法(或称为寻址方式),就可以发现

§ 第7章 更灵活的定位内存地址的方法 - 图13

学习建议

完成这章的所有问题