1. 处理字符问题
汇编中, 用'...' 的方式致命数据是以字符的形势给出的, 编译器将把他们转化为相对应的ASCII 码
assume cs:code,ds:data
data segment
db 'unIX'
db 'foRK'
data ends
code segment
start: mov al,'a'
mov bl,'b'
mov ax,4c00h
int 21h
code ends
end start
大小写转换
- 将第一个字符串, 小写字母转换为大写
- 将第二个字符串, 大写转换为小写
assume cs:code,ds:data
data segment
db 'BaSiC'
db 'iNfOrMaTiOn'
data ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
mov cx,5
s: mov al,[bx]
and al,11011111b
mov [bx],al
inc bx
loop s
mov bx,5
mov cx,11
s0: mov al,[bx]
or al,00100000b
mov [bx],al
inc bx
loop s0
mov ax,4c00h
int 21h
code ends
end start
逻辑与指令add dest,src
, 逻辑或指令or dest,src
2. [bx+idata] 方式寻址
含义: 表示一个内存单元, 它的偏移地址为(bx)+idata, bx 中的数值加上idata
例如:
mov ax,[bx+200]
- 将一个内存单元的内容送入ax
- 内存单元长度为2字节
- 内存单元段地址在ds 中, 偏移地址为200, 加上bx 中的数值
- 描述为: (ax)=((ds)*16+200+(bx))
其他用法
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
类似c 语言中的数组
- 将第一个字符串转换为大写
- 第二个转换为小写
assume cs:code,ds:data
data segment
db 'BaSiC'
db 'MinIX'
data ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
mov cx,5
s: mov al,[bx]
and al,11011111b
mov [bx],al
mov al,[5+bx]
or al,00100000b
mov [5+bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start
相当于c 语言中的数组
char a[5]="BaSiC";
char b[5]="MinIX";
main(){
int i;
i=0;
do{
a[i]=a[i]&0xDF;
b[i]=b[i]|0x20;
i++;
}
while(i<5);
}
3. SI 和DI寄存器
两个寄存器为变址寄存器
常执行与地址有关的操作
SI 和DI 是8086cpu 中和bx 功能相近的寄存器
区别为SI 和DI 不能够区分成两个8 位寄存器来使用
bx: 通用寄存器, 在计算存储器地址时, 常作为基址寄存器用
si: source index, 源变址寄存器
di: destination index, 目标变址寄存器
这三组代码含义相同
mov bx,0
mov ax,[bx]
mov si,0
mov ax,[si]
mov di,0
mov ax,[di]
====================
mov bx,0
mov ax,[bx+123]
mov si,0
mov ax,[123]
mov di,0
mov ax,[di+123]
例如, 用寄存器si 和di 实现将字符串'welcome to masm' 复制到它后面的数据区中
ds:si
指向要复制的原始字符串
ds:di
指向目的空间
使用循环完成复制
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
s: mov ax,[si]
mov [di],ax
add si,2
add di,2
loop s
mov ax,4c00h
int 21h
code ends
end start
4. [bx+si] 和[bx+di] 方式寻址
[bx+si]
表示一个内存单元
偏移地址为(bx)+(si), bx 中的数值加上si 的数值
指令mov ax,[bx+si]
含义
- 将一个内存单元的内容送入ax
- 这个内存单元长度为2 个字节
- 偏移地址为bx 中的数值加上si 中的数值
- 段地址在ds 中
- 数学化描述为(ax)=((ds)*16+(bx)+(si)), 其他写法
mov ax,[bx][si]
5. [bx+si+idata] 和[bx+di+idata]
[bx+si+idata]
表示一个内存单元
偏移地址为(bx)+(si)+idata, bx 中的数值加上si 数值再加idata
指令mov ax,[bx+si+idata]
含义
- 将一个内存单元内容送入ax
- 这个内存单元长度为2 个字节
- 偏移地址为bx 中的数值加si 数值再加idata, 段地址在ds 中
- 描述为(ax)=((ds)*16+(bx)+(si)+idata)
其他写法
mov ax,[bx+200+si]
mov ax,[200+bx+si]
mov ax,200[bx][si]
mov ax,[bx].200[si]
mov ax,[bx][si].200
mov ax,[bx][si]
6. 不同寻址方式的应用
形式 | 名称 | 特点 | 意义 | 示例 |
---|---|---|---|---|
[idata] | 直接寻址 | 用一个常量/立即数表示地址 | 用于直接定位一个内存单元 | mov ax,[200] |
[bx] | 寄存器间接寻址 | 一个变量表示内存地址 | 间接定位一个内存单元 | mov bx,0 mov ax,[bx] |
[bx+idata] | 寄存器相对寻址 | 一个变量和常量表示地址 | 可在一个起始地址的基础上用变了间接定位一个内存单元 | mov bx,4 mov ax,[bx+200] |
[bx+si] | 基址变址寻址 | 两个变量表示地址 | mov ax,[bx+si] | |
[bx+si+idata] | 相对基址变址寻址 | 两个变量和一个常量表示地址 | mov ax,[bx+si+200] |
案例1:
将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
s: mov al,[bx+3]
and al,11011111b
mov [bx+3],al
add bx,16
loop s
mov ax,4c00h
int 21h
code ends
end start
案例2:
将data 段中的每个单词都改为大写
assume cs:code,ds:data
data segment
db 'ibm '
db 'dec '
db 'dox '
db 'vax '
data ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s0: mov si,0
mov cx,3
s: mov al,[bx+si]
and al,110111111b
mov [bx+si],al
inc si
loop s
add bx,16
loop s0
mov ax,4c00h
int 21h
code ends
end start
4 个字符串, 看成一个4 行16 列的二维数组, 只要修改二维数组的每一行的前三列, 构造4*3 的二重循环
但是循环次数由cx 决定, cx 只有一个, 此段代码有问题
如何解决
将外层循环的cx 保存到dx 中, 再将cx 设置为内循环次数3, 内循环完, 再将dx 的值赋值给cx
s0: mov dx,cx ;将外层cx 保存到dx 中
mov si,0
mov cx,3 ;设置内层循环次数
s: mov al,[bx+si]
and al,110111111b
mov [bx+si],al
inc si
loop s
add bx,16
mov cx,dx ;恢复cx 外层循环次数
loop s0
但是寄存器只有14 个, 如果dx 也已经被使用, 还是有问题
如果使用固定的内存空间保存数据, 但是这个内存空间也会被占用的可能
最终的解决方法
使用栈来管理数据
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s0: push cx ;将外层循环cx 压栈
mov si,0
mov cx,3
s: mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s
add bx,16
pop cx ;从栈顶弹出原cx 值, 恢复cx
loop s0
7. 用于内存寻址的寄存器
正确的指令
mov ax,[bx]
mov ax,[bx+si]
mov ax,[bx+di]
mov ax,[bp]
mov ax,[bp+si]
mov ax,[bp+di]
mov ax,[bx]
mov ax,[si]
mov ax,[di]
mov ax,[bp]
mov ax,[bx+si]
mov ax,[bx+di]
mov ax,[bp+si]
mov ax,[bp+di]
mov ax,[bx+si+idata]
mov ax,[bx+di+idata]
mov ax,[bp+si+idata]
mov ax,[bp+di+idata]
只有bx, bp, si, di 可以用在[...] 对内存单元寻址
bx 以外的通用寄存器, 段寄存器不可以用在[...] 中
bx 和bp 区别
- bx 默认指ds 段
- bp 默认指ss 段
或者直接指定段地址
mov ax,ds:[bp]
mov ax,es"[bp]
8. 处理数据的位置和长度
数据位置的表达
立即数 | 寄存器 | 内存:段地址(SA) 和偏移地址(EA) |
---|---|---|
对于直接包含在机器指令中的数据, 称为立即数idata, 数据包含在指令中mov ax,1 mov al,'a'
|
指令要处理的数据在寄存器中, 在汇编指令中给相应的寄存器名mov ax,bx mov ds:[0],bx
|
指令要处理的数据在内存中, 由SA:EA 确定内存单元 |
要处理数据的长度
9. 寻址方式的案例
一条记录
姓名: yao
生日: 19800912
球衣号码: 15
场均得分: 32
球队: SHH
修改为:
球衣 -> 11
得分 -> 13
球队 -> HOU
mov ax,seg
mov ds,ax
mov bx,60h
mov word ptr [bx+0ch],11
mov word ptr [bx+0eh],13
mov si,0
mov byte ptr [bx+10h+si],'H'
inc si
mov byte ptr [bx+10h+si],'O'
inc si
mov byte ptr [bx+10h+si],'U'
如果是c 语言则是
#include <stdio.h>
struct Player{
char name[3];
char birthday[9];
int num;
int ppg;
char team[3];
};
struct Player yao={"Yao", "19800912", 15, 32, "SHH"}
int main()
{
int i;
yao.num = 11;
yao.ppg = 13;
i = 0;
yao.team[i] = "H";
i++;
yao.team[i] = "O";
i++;
yao.team[i] = "U";
return 0;
}
yao.team[i]: yao是一个变量名, 指明了结构体变量的地址, team 是一个名称, 指明数据项team 的地址, i 用来定位team 中的字符
用bx 定位整个结构体, 用idata 定位结构体中的某一个数据项, 用si 定位数据项中的元素
10. div 指令实现除法
- 被除数: 默认放在ax 或dx 和ax 中
- 除数: 8 位或16 位, 在寄存器或内存单元中
格式为
- div 寄存器
- div 内存单元
结果
被除数 | ax | dx 和ax |
---|---|---|
除数 | 8 位内存或寄存器 | 16 位内存或寄存器 |
商 | al | ax |
余数 | ah | dx |
指令 | 被除数 | 除数 | 商 | 余数 |
---|---|---|---|---|
div bl | (ax) | (bl) | (al) | (ah) |
div byte ptr ds:[0] | (ax) | ((ds)*16+0) | (al) | (ah) |
div byte ptr [bx+si+8] | (ax) | ((ds)*16+(bx) +(si)+8) | (al) | (ah) |
div bx | (dx)*10000H+(ax) | (bx) | (ax) | (dx) |
div word ptr es:[0] | (dx)*10000H+(ax) | ((ds)*16+0) | (ax) | (dx) |
div word ptr [bx+si+8] | (dx)*10000H+(ax) | ((ds)*16+(bx) +(si)+8) | (ax) | (dx) |
注意: 提前在默认的寄存器中设置好被除数, 且不能用在别处
11. dup 设置内存空间
dup 和db, dw, dd 等数据定义伪指令配合使用, 用来进行数据的重复
指令 | 功能 | 相当于 |
---|---|---|
db 3 dup(0) | 定义3 个字节, 值都是0 | db 0,0,0 |
db 3 dup(0,1,2) | 定义9 个字节, 由0,1,2 重复3 次 | db 0,1,2,0,1,2,0,1,2 |
db 3 dup('abc','ABC') | 定义18 个字节, 构成'abcABCabcABCabcABC' | db 'abcABCabcABCabcABC' |