不同的寻址方式的灵活应用
1) [idata]用一个常量来表示地址,可以直接定位一个内存单元。(注意编译器的差别)
2)[bx]用一个变量来表示内存地址,可以间接定位一个内存单元。
3)[bx + idata]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元。
4)[bx + si]用两个变量表示地址。
5)[bx + si + idata]用两个变量和一个常量表示地址。
例1:将datasg段中每个单词的头一个字母改为大写字母。
assume cs:codesg, da:datasg
datasg segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
datasg ends
datasg中定义了6个字符串,每个长度为16个字节。(后面用空格填充)因为它们是连续存放的可以将6个字符串看成一个6行16列的二维数组。也就变成了需要修改每一行的第4列的字符。(相对于首行的偏移地址为3)
assume cs:codesg, da:datasg
datasg segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax
mov bx, 0
mov cx, 6
s: mov al, [bx + 3]
and al, 11011111B //2进制
mov [bx + 3], al
add bx, 16
loop s
mov ax, 4C00H
int 21H
codesg ends
end start
例2:将datasg段中每个单词改为大写字母(没有保存寄存器cx导致循环错误)
assume cs:codesg, da:datasg
datasg segment
db 'file '
db 'edit '
db 'search '
db 'view '
datasg ends
看作数组的话要进行双重循环,即4行n列,即循环4*3次。
assume cs:codesg, da:datasg
datasg segment
db 'fil '
db 'edi '
db 'sea '
db 'vie '
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax
mov cx, 4
mov si, 0
//外层
s0: mov cx, 3 //重置cx 计数
mov bx, 0 //重置列
//内层循环,循环行
s: mov al, [bx + si]
and al, 11011111B
mov [bx + si], al
inc bx
loop s
add si, 16 //增加行
loop s0
mov ax, 4c00H
int 21H
codesg ends
end start
以上程序有问题,cx代表了loop循环的计数,在内层循环的时候会导致内从循环覆盖了外层循环。因为不能多用一个计数器,所以我们要在内层循环开始时,保存外层循环的cx值。在执行外层循环loop前,再恢复外层循环的cx值。
改进(用dx寄存器保存cx中的值):
assume cs:codesg, da:datasg
datasg segment
db 'fil '
db 'edi '
db 'sea '
db 'vie '
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax
mov cx, 4
mov si, 0
//外层
s0: mov dx, cx //保存外层循环的计数器的值,第一次会保存初始化的值,后面会保存减1的值
mov cx, 3 //重置cx 计数
mov bx, 0 //重置列
//内层循环,循环行
s: mov al, [bx + si]
and al, 11011111B
mov [bx + si], al
inc bx
loop s
add si, 16 //增加行,换行
mov cx, dx //将计数器的值拿出
loop s0 //这里会判断计数器的值,并减1
mov ax, 4c00H
int 21H
codesg ends
end start
改进(开辟一段空间来保存cx的值):
assume cs:codesg, da:datasg
datasg segment
db 'fil '
db 'edi '
db 'sea '
db 'vie '
dw 0 //定义一个字来暂存cx
datasg ends
codesg segment
start: mov ax, datasg
mov ds, ax
mov cx, 4
mov si, 0
//外层
s0: mov ds:[0040H], cx //保存外层循环的计数器的值,第一次会保存初始化的值,后面会保存减1的值(用开辟地内存保存)
mov cx, 3 //重置cx 计数
mov bx, 0 //重置列
//内层循环,循环行
s: mov al, [bx + si]
and al, 11011111B
mov [bx + si], al
inc bx
loop s
add si, 16 //增加行
mov cx, ds:[0040H] //将计数器的值拿出(用开辟地内存保存)
loop s0 //这里会判断计数器的值,并减1
mov ax, 4c00H
int 21H
codesg ends
end start
一般来说,在需要暂存数据的时候,我们都应该使用栈。
改进(用栈来保存):
assume cs:codesg, da:datasg, ss:stacksg
datasg segment
db 'fil '
db 'edi '
db 'sea '
db 'vie '
datasg ends
stacksg segment
dw 0,0,0,0,0,0,0,0 //定义一个段,用来做栈段,容量为16个字节
stacksg ends
codesg segment
start: mov ax, datasg
mov ds, ax
//栈设置
mov ax, stacksg
mov ss, ax
mov sp, 16 //栈底
mov cx, 4
mov si, 0
//外层
s0: push cx //保存外层循环的计数器的值,第一次会保存初始化的值,后面会保存减1的值(入栈)
mov cx, 3 //重置cx 计数
mov bx, 0 //重置列
//内层循环,循环行
s: mov al, [bx + si]
and al, 11011111B
mov [bx + si], al
inc bx
loop s
add si, 16 //增加行
pop cx //将计数器的值拿出(出栈)
loop s0 //这里会判断计数器的值,并减1
mov ax, 4c00H
int 21H
codesg ends
end start
例3:编程,将datasg段中每个单词的前4个字母改为大写字母
assume cs:codesg, da:datasg, ss:stacksg
datasg segment
db '1.display '
db '2.brows '
db '3.replace '
db '4.modify '
dw 0 //定义一个字来暂存cx
datasg ends
stacksg segment
dw 0,0,0,0,0,0,0,0 //定义一个段,用来做栈段,容量为16个字节
stacksg ends
codesg segment
//数据段
start: mov ax, datasg
mov ds, ax
//栈段
mov ax, stacksg
mov ss, ax
mov sp, 16 //栈底
mov bx, 0
mov si, 0
mov cx, 4
//外层循环
s: push cx //保存外从循环的计数器值
mov cx, 4
s0: mov al, [bx + si + 3]
and al, 11011111B //转大写
mov [bx + si + 3], al
inc si
loop s0
mov si, 0
mov add, 16
pop cx
loop s
mov ax, 40cH
int 21H
codesg ends
end start