更灵活定位内存地址
1.SI和DI
SI和DI是8086CPU中和bx功能相近的寄存器,但是SI和DI不能够分为两个8位寄存器使用。
下面三组指令实现了相同的功能
(1)
1 | mov bx,0 |
(2)
1 | mov si,0 |
(3)
1 | mov di,0 |
问题:用寄存器SI和DI实现将字符串’welcome to masm!’复制到它后面的数据区中。
分析:我们在处理数据之前首先要搞清楚数据存储在什么地方,也就是说数据的内存地址。
因为’welcome to masm!’从偏移地址0开始存放,长度为16个字节,所以,它后面的数据区的偏移地址为16,就是字符串所要存放的空间。
我们用ds:si指向要复制的源始字符串,用ds:di指向复制的目的空间,然后用一个循环来完成复制。
代码:
1 | assume cs:codesg,ds:datasg |
1.1 [bx+si] 和 [bx+di]
[bx+si]和[bx+di]的含义类似,我们以[bx+si]为例。
[bx+si]表示一个内存单元,它的偏移地址为(bx)+(si),即bx中的数值加上si中的数值
指令mov ax,[bx+si]
的数学化的描述为:(ax) = ((ds)*16 + (bx) + (si))
问题:用Debug查看内存,结果如下:
2000:1000 BE 00 06 00 00 00 …
写出下面程序执行后,cx、ax中的内容
1 | mov ax,2000h |
1.2 [bx+si+idata]和[bx+di+idata]
[bx+si+idata]和[bx+di+idata]的含义相似。我们以[bx+si+idata]为例。
[bx+si+idata]表示一个内存单元,它的偏移地址为(bx)+(si)+idata。
指令mov ax,[bx+si+idata]
的数学化描述为(ax) = ((ds)*16 + (bx) +(si) + idata)
该指令可以写成如下格式
1 | mov ax,[bx+200+si] |
问题:用Debug查看内存,结果如下
2000:1000 BE 00 06 00 6A 22 …
写出下面的程序执行后,ax、bx、cx中的内容。
1 | mov ax,2000h |
2.不同寻址方式的灵活应用
将寻址方式总结为以下几种方法:
- [idata]用一个常量来表示地址,可用于直接定位一个内存单元(直接寻址)
- [bx]用一个变量来表示内存单元,可用于间接定位一个内存单元(间接寻址)
- [bx+idata]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元(基址寻址)
- [si+idata](变址寻址)
- [bx+si]用两个变量表示地址(基址加变址寻址)
- [bx+si+idata]用两个变量和一个常量表示地址
可以看到:
从[idata]一直到[bx+si+idata],我们可以用更加灵活的方式来定位一个内存单元的地址。这使我们可以从更加结构化的角度来看待所要处理的数据。
2.1 单重循环处理
问题1:编程,将datasg段中每个单词的头一个字母改为大写字母。
1 | assume cs:codesg,ds:datasg |
分析:datasg中的数据的存储结构,如图:
我们需要进行6次循环,用一个变量R定位行,用常量3定位列。处理过程如下:
bx先存放第一行的地址
mov cx,6
;因为总共有6行
s: 改变第bx行,第三列的字母为大写。改变bx的值使它指向下一行的地址
loop s
我们用bx作变量,定位每行的起始地址,用3定位要修改的列,用[bx+idata]的方式来对目标单元进行寻址。
代码实现:
1 | assume cs:codesg,ds:datasg |
debug效果:
2.2 多重循环处理
问题2:将datasg段中每个单词改为大写字母。
1 | assume cs:codesg,ds:datasg |
分析:因为它们是连续存放的,我们可以将这4个字符串看成一个4行16列的二维数组。按照要求,我们需要修改每一个单词,即二维数组的每一行的前三列。
我们需要进行4*3次的二重循环(循环嵌套),用变量R定位行,变量C定位列
- 外层循环按行来进行
- 内层循环按列来进行
我们首先用R定位第一行,然后循环修改R行的前三列;然后再用R定位到下一行,再次循环修改R行的前三列
如此重复直到所有数据修改完毕。
我们用bx来作变量,定位每行的起始地址,用si定位要修改的列,用[bx+si]的方式来对目标单元进行查找
关键问题:我们进行二重循环,但是loop指令默认cx为循环计数器,在内层循环时会覆盖外层循环的循环计数值。
我们应该在每次开始内层循环的时候,将外层循环的cx中的数值保存起来,在执行外层循环的loop指令前,在恢复外层循环的数值。
代码:
1 | assume cs:codesg,ds:datasg |
上面的程序用dx来暂时存放cx中的值;如果在内层循环中,dx寄存器也被使用,该怎么办?
CPU中的寄存器数量毕竟是有限的,如8086CPU只有14个寄存器。
我们希望寻找一个通用的方案,来解决这种在编程中经常会出现的问题。
我们可以考虑将需要暂存的数据放到内存单元中,需要使用的时候,再从内存单元中恢复。这样我们就需要开辟一段内存空间。
1 | assume cs:codesg,ds:datasg |
但是上面的做法有些麻烦,因为如果需要保存多个数据的时候,必须要记住哪些数据放在了哪个单元中,这样程序容易混乱。
我们使用内存来暂存数据,这一点是确定了的,但是值得推敲的是,我们用怎样的结构来保存这些数据,从而使得程序更加清晰。
一般来说,在需要暂存数据的时候,我们都应该使用栈。
1 | assume cs:codesg,ds:datasg,ss:stacksg |
3.课后实验
题目:编程,将datasg段中每个单词的前四个字母改为大写字母。
1 | assume cs:codesg,ds:datasg,ss:stacksg |
答案:
1 | assume cs:codesg,ds:datasg,ss:stacksg |
debug结果:
注意and运算时,后面要加上b表明是二进制数据