第七章 更灵活的定位内存地址的方法 其二
7.10 不同的寻址方式的灵活应用
如果我们比较一下前面用到的几种定位内存地址的方法(可称为寻址方式),就可以发现有以下几种方式:
1)[idata]用一个常量来表示地址,可用于直接定位一个内存单元。
2)[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元。
3)[bx+idata]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元。
4)[bx+si]用两个变量表示地址。
5)[bx+si+idata]用两个变量和一个常量表示地址。
1 assume cs:codesg,ds:datasg 2 datasg segment 3 db 'ibm ' 4 db 'dec ' 5 db 'dos ' 6 db 'vax ' 7 datasg ends 8 9 codesg segment 10 start: mov ax,datasg 11 mov ds,ax 12 mov bx,0 13 14 mov cx,4 15 s0: mov si,0 16 mov cx,3 17 s: mov al,[bx+si] 18 and al,11011111b 19 mov [bx+si],al 20 21 inc si 22 23 loop s 24 25 add bx,16 26 loop s0 27 28 mov ax,4c00h 29 int 21h 30 codesg ends 31 end start
这个程序是有错误的,运行之后会进入死循环
问题在于cx的使用,我们进行二重循环,却只用了一个循环计数器,造成在进行内层的时候覆盖了外层循环的循环计数器。
多用一个计数器又不可能,因为loop指令默认cx为循环计数器
怎么办呢?
我们应该在每次开始内层循环的时候,将外层循环的cx中的数值保存起来,在执行外层循环的loop指令前,再恢复外层循环的cx数值。
我们可以用寄存器dx来临时保存cx中的值。
上面的程序用dx来暂时存放cx中的值
如果在内层循环中,dx寄存器也被使用,该怎么办?
我么似乎可以使用别的寄存器,但是cpu中的寄存器数量毕竟是有限的,如8086CPU只有14个寄存器。
在上面的程序中:
si、cx、ax、bx,显然不能用来暂存cx中的值,因为这些寄存器在循环中也要使用。
cs、ip、ds也不能用,因为cs:ip时刻指向当前指令,ds也指向datasg段;
可用的就只有:dx、di、es、ss、sp、bp等寄存器了
可是如果循环中的程序比较复杂,这些寄存器也都被使用的话,那么该如何?
我们可以考虑将需要暂存的数据放到内存单元中,需要使用的时候,再从内存单元中恢复。这样我们就需要再开辟一段内存空间。
程序如下:
1 assume cs:codesg,ds:datasg 2 datasg segment 3 db 'ibm.............' 4 db 'dec.............' 5 db 'dos.............' 6 db 'vax.............' 7 dw 0 ;定义一个字,用来保存cx 8 datasg ends 9 10 codesg segment 11 start: mov ax,datasg 12 mov ds,ax 13 14 mov bx,0 15 16 mov cx,4 17 s0: mov ds:[40H],cx ;将外层循环的cx值保存在datasg:40H单元中 18 mov si,0 19 mov cx,3 ;cx设置为内存循环的次数 20 s: mov al,[bx+si] 21 and al,11011111b 22 mov [bx+si],al 23 inc si 24 loop s 25 26 add bx,16 27 mov cx,ds:[40H] ;用datasg:40H单元中的值恢复cx 28 loop s0 ;外层循环的loop指令将cx中的计数值减 1 29 30 mov ax,4c00h 31 int 21h 32 codesg ends 33 end start
上面的程序中,用内存单元来保存数据;
可是上面的做法却有些麻烦,因为如果需要保存多个数据的时候,读者必须要记住数据放到了哪个单元中,这样程序容易混乱。
一般来说,在需要暂存数据的时候,我们都应该使用栈,栈空间在内存中,采用相关的指令,如:push、pop等,可对其进行特殊的操作。
改进程序如下:
1 assume cs:codesg,ds:datasg,ss:stacksg 2 datasg segment 3 db 'ibm ' 4 db 'dec ' 5 db 'dos ' 6 db 'vax ' 7 datasg ends 8 9 stacksg segment ;定义一个段,用来作栈段,容量为16个字节 10 dw 0,0,0,0,0,0,0,0 11 stacksg ends 12 13 codesg segment 14 start: mov ax,stacksg 15 mov ss,ax 16 mov sp,16 17 mov ax,datasg 18 mov ds,ax 19 20 mov bx,0 21 22 mov cx,4 23 s0: push cx ;将外层循环的cx值压栈 24 mov si,0 25 mov cx,3 ;cx设置为内层循环的次数 26 s: mov al,[bx+si] 27 and al,11011111b 28 mov [bx+si],al 29 inc si 30 loop s 31 32 add bx,16 33 pop cx ;从栈顶弹出原cx的值,恢复cx 34 loop s0 ;外层循环的loop指令将cx中的计数值减 1 35 36 mov ax,4c00h 37 int 21h 38 codesg ends 39 end start
1 assume cs:codesg,ds:datasg,ss:stacksg 2 stacksg segment 3 dw 0,0,0,0,0,0,0,0 4 stacksg ends 5 6 datasg segment 7 db '1. display......' 8 db '2. brows........' 9 db '3. replace......' 10 db '4. modify.......' 11 datasg ends 12 13 codesg segment 14 start: mov ax,stacksg 15 mov ss,ax 16 mov sp,16 17 18 mov ax,datasg 19 mov ds,ax 20 21 mov bx,0 22 mov cx,4 23 s: mov si,0 24 push cx 25 mov cx,4 26 27 s0: mov al,[bx+si+3] 28 and al,11011111b 29 mov [bx+si+3],al 30 inc si 31 loop s0 32 33 add bx,16 34 pop cx 35 loop s 36 37 mov ax,4c00h 38 int 21h 39 codesg ends 40 end start