# 第七章 更灵活地定位内存地址
第七章 更灵活地定位内存地址
本章主要讲解一些更灵活的定位内存地址的方法和相关的编程方法
-
and 和 or 指令
-
and 指令
- 如:mov al,01100011B
and al,00111011B
执行后:
al=00100011B - 通过and指令可将操作对象的相应位设为0,其他位保持不变
例如al的第6位设为0:and al,10111111B
例如al的第7位设为0:and al,01111111B
例如al的第0位设为0:and al,11111110B
- 如:mov al,01100011B
-
or指令,逻辑或运算,按位进行或运算
- 如:mov al,01100011B
or al,00111011B
执行后:
al=01111011B - 通过该指令可将操作对象的相应位设为1,其他位不变
or al,01000000B;将al的第6位设为1
or al,10000000B;将al的第7位设为1
or al,00000001B;将al的第0位设为1
- 如:mov al,01100011B
-
以字符形式给出的数据
- 在汇编程序中,可以使用'×××'的方式指明数据是以字符的形式给出的
2. 编译器会将它们转化为相应的ASCII码
3. 例如- db 'unIX' ; 相当于:db 75H,6EH,49H,58H
'u'、'n'、'I'、'X' 的ASCII码分别为75H,6EH,49H,58H - mov al,'a' ; 相当于:mov al,61H
'a'的ASCII码为61H
- ASCII码中,大写字母和小写字母之间的规律
小写字母=大写字母+32
小写字母=大写字母+20H
大写字母从41H开始排,小写字母从61H开始排
- db 'unIX' ; 相当于:db 75H,6EH,49H,58H
- 在汇编程序中,可以使用'×××'的方式指明数据是以字符的形式给出的
-
大小写转换的问题
- 方案一:
- 识别出是该字节是表示一个的大写英文字符,还是小写的
用于条件判断的汇编程序,目前还没有学到 - 根据+20H 或者 -20H进行大小写转换
- 识别出是该字节是表示一个的大写英文字符,还是小写的
- 方案二:
1. 若全部转化为大写,则将第5位置0
and al,11011111B
2. 若全部转化为小写,则将第5位置1
or al,00100000B
- 方案一:
-
-
[bx+常数]
mov ax,[bx+200]的含义:-
将一个内存单元的内容送入ax,这个内存单元的长度为2字节,存放一个入一个子单元
该字单元的偏移地址为bx中的数值加上200,段地址在ds中
-
也可以写成
- mov ax,200[bx]
- mov ax,[bx].200
-
-
用[bx+idata]的方式进行数组的处理
在codesg中填写代码,将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写-
我们观察datasg段中的两个字符串,一个的起始地址为0,另一个的起始地址为5
-
我们可以将这两个字符串看作两个数组,一个从0地址开始存放,另一个从5开始存放
-
我们可以用[0+bx]和[5+bx]的方式在同一个循环中定位这两个字符串中的字符
-
注意这个数组的定位方式,对比C语言
C语言的数组定位方式:a[i],b[i], a、b是地址常量
汇编语言的数组定位方式:0[bx],5[bx]
所以:[bx+常数]的方式为高级语言实现数组提供了便利的机制
改进版大小写转换:
assume cs:codesg,ds:datasg datasg segment db 'BaSiC' db 'MinIX' datasg ends codesg segment start: mov ax,datasg mov ds,ax mov bx,0 mov cx,5 ;做5次循环 circ: mov al,[bx] and al,11011111b mov [bx],al mov al,[bx+5];等价于mov al,5[bx];等价于mov al,[bx].5 or al,00100000b mov 5[bx],al inc bx loop circ mov ax,4c00h int 21h codesg ends end start
-
-
SI和DI
已经学过的10个寄存器:AX、BX、CX、DX、DS、CS、SS、ES、IP、SP
-
SI和DI是8086CPU中和bx功能相近的寄存器,bx不够用,所以引进了SI和DI
-
SI和DI(16位)不能够分成两个8位寄存器来使用【和bx的区别】
-
默认段寄存器为ds【和bx的形同】
-
下面三组指令也实现了相同的功能
-
mov bx,0
mov ax,[bx]
-
mov si,0
mov ax,[si]
-
mov di,0
mov ax,[di]
-
mov bx,0
mov ax,[bx]
-
mov si,0
mov ax,[si]
-
mov di,0
mov ax,[di]
-
-
用寄存器SI和DI实现将字符串'welcome to masm!'复制到它后面的数据区中
分析:
当进行数据处理时,需弄清楚数据存放的位置,也就是数据的内存地址
通常用ds:si指向要复制的源始字符串
通常用ds:di指向要复制的目的空间
注意si、di是16位寄存器,循环中自增时,应该+2
assume cs:code,ds:data data segment db 'welcome to masm!' db '................' data ends code segment start: mov ax,data mov ds,ax mov si,0 mov di,16 mov cx,8 circ: mov ax,[si] mov [di],ax add si,2 add di,2 loop circ mov ax,4c00h int 21h code ends end start
优化版本:
assume cs:code,ds:data data segment db 'welcome to masm!' db '................' data ends code segment start: mov ax,data mov ds,ax mov si,0 mov cx,8 circ: mov ax,0[si] mov 16[si],ax add si,2 loop circ mov ax,4c00h int 21h code ends end start
-
[bx+si]和[bx+di]
- [bx+si]和[bx+di]的含义类似,我们以[bx+si]为例进行讲解
[bx+si]表示一个内存单元,它的偏移地址为bx中的数值加上si中的数值
它的段地址在ds中
- [bx+si]也可以写成[bx][si]
-
[bx+si+常数]和[bx+di+常数]
-
以[bx+Si+常数]为例讲解
[bx+si+常量]表示一个内存单元,偏移地址为bx的值+si的值+常数
-
指令mov ax,[bx+si+常数]也可以写成如下形式
-
mov ax,200[bx+si]
- mov ax,200[bx][si]
-
mov ax,[bx].200[si]
- mov ax,[bx][si].200(数字在后面要加点)
-
-
不同的寻址方式的灵活应用
-
总结几种定位内存的方法
- ds:[常数] 【直接寻址】
用一个常量来表示地址,可用于直接定位一个内存单元
-
[bx] 【寄存器间接寻址】
用一个寄存器的值来表示内存地址,可以间接定位一个内存单元
-
[bx+常数]
用一节寄存器的值和常量表示内存地址,可在一个起始地址的基础上用变量间接定位一个内存单元
- [bx+si]
-
[bx+si+常数]
-
-
编程,给定数据段data,将data段中每个单词的头一个字母改写成大写字母
assume cs:code,ds:data data segment db '1. file ' db '2. edit ' db '3. search ' db '4. view ' db '5. options ' db '6. help ' data ends code segment start: mov ax,data mov ds,ax mov bx,0 mov cx,6 circ: mov al,[bx+3] # 发现第一个字母都在第三个位置上(从0开始) and al,11011111b mov [bx+3],al add bx,16 # 每个字符串16个字节,每次循环一个字符串 loop circ mov ax,4c00h int 21h code ends end start
-
编程,给定数据段data,将data段中的每个单词改为大写字母
-
【loop指令cx-1之后,在判断是否为0】
-
双重循环用汇编怎么实现?
应该在每次开始内循环的时候,将外层循环的cx的值保存起来,在执行外层循环的loop指令前,在恢复外层循环的cx数值。 可以用寄存器来临时保存,也可以用栈空间(内存)保存【没有多余的寄存器】 更好的方法是使用:栈 * 使用寄存器实现 ```assembly assume cs:code,ds:data data segment ``` db 4,4,6,4,7,4;单词的字母数 db ' ';补齐 db '1. file ' db '2. edit ' db '3. search ' db '4. view ' db '5. options ' db '6. help ' data ends code segment start: mov ax,data mov ds,ax mov bx,16 mov si,0 mov di,0 mov cx,6;外层循环6次 outer:;外层循环 mov dx,cx;用寄存器将外层循环的次数保存,C语言中是用栈来保存的 mov cx,0 mov cl,[di];内循环的次数 inner:;内层循环 mov al,[bx][si+3] and al,11011111b mov [bx][si+3],al inc si loop inner add bx,16 mov si,0 inc di mov cx,dx;恢复外层循环的次数 loop outer mov ax,4c00h int 21h code ends end start ``` * 使用栈实现【更好的方法】 ```assembly assume cs:code,ds:data,ss:stack data segment ``` db 4,4,6,4,7,4;单词的字母数 db ' ';补齐 db '1. file ' db '2. edit ' db '3. search ' db '4. view ' db '5. options ' db '6. help ' data ends stack segment dw 1,2,3,4,5,6,7,8 stack ends code segment start: mov ax,data mov ds,ax mov ax,stack mov ss,ax mov sp,16 mov bx,16 mov si,0 mov cx,6;外层循环6次 outer:;外层循环 push cx;将外层循环的次数保存 mov cx,0 mov cl,[di];内循环的次数 inner:;内层循环 mov al,[bx][si+3] and al,11011111b mov [bx][si+3],al inc si loop inner add bx,16 mov si,0 inc di pop cx;恢复外层循环的次数 loop outer mov ax,4c00h int 21h code ends end start ```
实验
问题:编程,将datasg段中的每个单词的前4个字母改为大写字母
assume cs:codesg,ss:stacksg,ds:datasg
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg endsdatasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg endscodesg segment
start:
codesg ends
end start
解: ```assembly assume cs:codesg,ss:stacksg,ds:datasg stacksg segment dw 0,0,0,0,0,0,0,0 stacksg ends datasg segment db '1. display ' db '2. brows ' db '3. replace ' db '4. modify ' datasg ends codesg segment start:mov ax,datasg mov ds,ax mov bx,3 mov cx,4 outer: push cx # 采用堆栈双重循环 mov cx,4 inner: mov al,[bx] and al,11011111b mov [bx],al inc bx loop inner add bx,12 # 注意最后一次多加了 1,故需加 16 - 3 - 1 = 12 pop cx loop outer mov ax,4c00h int 21h codesg ends end start
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构