汇编程序中,用 '……' 的方式指明数据是以字符的形式给出的,编译器将 把它们转化为相对应的ASCII码
assume ds:data data segment db 'unIX' data ends
概念
大写 二进制 小写 二进制 A 01000001 a 01100001 B 01000010 b 01100010 C 01000011 c 01100011 D 01000100 d 01100100
小写字母的ASCII码值比大写字母的ASCII码值大20H ,所以:
大写+20h->小写
小写-20h->大写
逻辑指令
b转换为B
and:逻辑与指令 and dest src
全为1则为1,其他都为0
0110 0010 (b) and 1101 1111 -------------- = 0100 0010 (B)
I转换为i
or:逻辑或指令 or dest src
全为0则为0,其他都为1
0100 1001 (I) or 0010 0000 -------------- = 0110 1001 (i)
实际转换
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'iNfOrMaTiOn' //内存中保存的都是16进制的ascii码值
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
//第一个字符串,小写字母全部转为大写字母
mov bx,0
mov cx,5 //5个字符循环5次
s: mov al,[bx] //将第一个字符放到al寄存器里面
and al,11011111b //进行逻辑与的操作,可以将小写字母转为大写字母
mov [bx],al //再将结果写回到原来的地址处
inc bx
loop
//第二个字符串,大写字母全部转为小写字母
mov bx,5
mov cx,11
s0: mov al,[bx]
or al,00100000b //进行逻辑或的操作,可以将大写字母转为小写字母
mov [bx],al
inc bx
loop s
mov ax,4c00h
int 21h
codesg ends
end star
[bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata(bx中的数值加上idata)
mov ax,[bx+200] / mov ax, [200+bx] 的含义 ;
1.将一个内存单元的内容送入ax ;
2.这个内存单元的长度为2字节(字单元),存放一个字 ;
3.内存单元的段地址在ds中,偏移地址为200加上bx中的数值 ;
4.数学化的描述为: (ax)=((ds)*16+200+(bx)
[bx+200]的其他写法:
200[bx],[bx].200
第一个字符串转化为大写
第二个字符串转化为小写
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 ;循环执行5次
mov ax,0 ;初始化ax寄存器
s: mov al,[bx] ;将第一串字符中的第一个字符放入al寄存器中
and al,11011111b ;将小写字符转为大写字符
mov [bx],al ;再写回到原来的字符串
mov al,[bx+5] ;再来处理第二个字符串
or al,00100000b ;将大写字符转换为小写字符
mov [bx+5],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start
对比C语言实现大小写转换
char a[5]="BaSiC";
char b[5]="MinIX";
int main()
{
int i;
i=0;
do
{
a[i]=a[i]&0xDF;
b[i]=b[i]|0x20;
i++;
}
while(i<5);
}
主要用于存放存储单元在段内的偏移量
SI——Source Index 源变址寄存器
DI——Destination Index 目的变址寄存器
[bx+si]表示一个内存单元 ;
偏移地址为(bx)+(si)(即bx中的数值加上si中的数值)。
指令mov ax,[bx+si]的含义 ;
1.将一个内存单元的内容送入ax
2.这个内存单元的长度为2字节(字单元),存放一个字
3.偏移地址为bx中的数值加上si中的数值
4.段地址在ds中
5.数学化的描述 : (ax)=( (ds)*16+(bx)+(si) )
6.其他写法 :mov ax,[bx][si
[bx+si+idata]表示一个内存单元
偏移地址为(bx)+(si)+idata,即bx中的数值加上si中的数值再加上idata
指令mov ax,[bx+si+idata]的含义
1.将一个内存单元的内容送入ax
2.这个内存单元的长度为2字节(字单元),存放一个字
3.偏移地址为bx中的数值加上si中的数值再加上idata,段地址在ds中
4.数学化的描述 ; (ax)=( (ds)*16+(bx)+(si)+idata )
5.其他写法
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
1.[idata]:直接寻址,用于直接指定一个内存单元
2.[bx]:寄存器间接寻址,用于间接定位一个内存单元
3.[bx+idata]:寄存器相对寻址,可在一个起始地址的基础上用变量间接定位一个内存单元
4.[bx+si]:基址变址寻址,用两个变量表示地址
5.[bx+si+idata]:相对基址变址寻址
循环次数是由寄存器cx的值决定的。而cx只有一个,那么面对多重循环的时候,要怎么解决这个问题呢?
1.用其他寄存器暂时把cx里的值存储起来
//编程将datasg段中每个单词改为大写字母
assume cs:codesg,ds:datasg
datasg segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0 //初始化bx
mov cx,4 //有4个字符串
//要有两层循环
s0: mov dx,cx //将外层循环的cx值保存在dx中
mov si,0 //源地址
mov cx,3 //内层循环3次,因为一行要处理3个字符
s: mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s
add bx,4 //4个位置之后,是下一个字符串
mov cx,dx//再把外层循环要用的取出来
loop s0
mov ax,4c00h
int 21h
codesg ends
end starttrh
2.用固定的内存空间保存数据
//编程将datasg段中每个单词改为大写字母
assume cs:codesg,ds:datasg
datasg segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0 //初始化bx
mov cx,4 //有4个字符串
//要有两层循环
s0: mov ds:[40h],cx //将外层循环的cx值保存在datasg:40h单元中
mov si,0 //源地址
mov cx,3 //内层循环3次,因为一行要处理3个字符
s: mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s
add bx,4 //4个位置之后,是下一个字符串
mov cx,ds:[40h]//再把外层循环要用的cx值取出来
loop s0
mov ax,4c00h
int 21h
codesg ends
end starttrh
上面的两种方法都有一定的问题:
1.寄存器数量太少,或者使用某个寄存器的时候,这个寄存器正在做其他事情
2.内存空间单元可能正在被使用
3.使用栈来保存数据
//编程将datasg段中每个单词改为大写字母
assume cs:codesg,ds:datasg,ss:stack
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
datasg segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
datasg ends
codesg segment
start:
mov ax,stack
mov ss,ax
mov sp,16 //初始化ss和sp寄存器
mov ax,datasg
mov ds,ax
mov bx,0 //初始化bx
mov cx,4 //有4个字符串
//要有两层循环
s0: push cx //将外层循环的cx值压栈
mov si,0 //源地址
mov cx,3 //内层循环3次,因为一行要处理3个字符
s: mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s
add bx,4 //4个位置之后,是下一个字符串
pop cx //从栈顶弹出原cx的值,恢复cx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
1.BX——Base 基地址寄存器
主要用于存放存储单元在段内的偏移量:
2.SI——Source Index 源变址寄存器
3.DI——Destination Index 目的变址寄存器
4.BP——Base Pointer 基指针寄存器
只有bx、bp、 si、di可以用在[...]对内存单元寻址
bx以外的通用寄存器、 段寄存器不可以用在[...] 中
//正确的指令 mov ax,[bx] mov ax,[bx+si] mov ax,[bx+di] mov ax,[bp] mov ax,[bp+si] mov ax,[bp+di] //错误的指令 mov ax,[cx] mov ax,[ax] mov ax,[dx] mov ax,[ds]
//错误的指令 mov ax,[bx+bp] mov ax,[si+di]
这四个寄存器可以单独使用,也可以混合着使用。但是以上两种方式不可行
原因:
bx,bp两个相当于是基地址,自然不能两个基地址相加
si,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寄存器的区别:
1.bx默认指ds作为段地址
2.bp默认指ss作为段地址
但是也可以给这二者指定某一个寄存器作为段地址
mov ax,[bp] (ax)=((ss)*16+(bp)) mov ax,ds:[bp] (ax)=((ds)*16+(bp)) mov ax,es:[bp] (ax)=((es)*16+(bp)) mov ax,[bx] (ax)=((ds)*16+(bx)) mov ax,ss:[bx] (ax)=((ss)*16+(bx)) mov ax,[bp+idata] (ax)=((ss)*16+(bp)+idata) mov ax,[bp+si] (ax)=((ss)*16+(bp)+(si)) mov ax,[bp+si+idata] (ax)=((ss)*16+(bp)+(si)+idata
对于直接包含在机器指令中的数据,称为立即数 (idata ),数据包含在指令z中
mov ax,1 add bx,2000h or bx,00010000b mov al,'a'
指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。
mov ax,bx mov ds,ax push bx mov ds:[0],bx push ds mov ss,ax mov sp,ax
内存:段地址(SA)和偏移地址(EA)
指令要处理的数据在内存中,由SA:EA确定内存单元
mov ax,[0] mov ax,[di] mov ax,[bx+8] mov ax,[bx+si] mov ax,[bx+si+8] //段地址默认在ds中 mov ax,[bp] mov ax,[bp+8] mov ax,[bp+si] mov ax,[bp+si+8] //段地址默认在ss中 mov ax,ds:[bp] (ax)=((ds)*16+(bp)) mov ax,es:[bx] (ax)=((es)*16+(bx)) mov ax,ss:[bx+si] (ax)=((ss)*16+(bx)+(si)) mov ax,cs:[bx+si+8] (ax)=((cs)*16+(bx)+(si)+8) //显性的给出存放段地址的寄存器
mov ax,1 mov bx,ds:[0] mov ds,ax mov ds:[0],ax inc ax add ax,100
因为上述寄存器都是16位的寄存器,所以对数据的处理都是字操作
mov al,1 mov al,bl mov al,ds:[0] mov ds:[0],al inc al add al,10
因为上述寄存器都是8位的寄存器,所以对数据的处理都是字节操作
mov word ptr ds:[0],1 inc word ptr [bx] inc word ptr ds:[0] add word ptr [bx],2 mov byte ptr ds:[0],1 inc byte ptr [bx] inc byte ptr ds:[0] add byte ptr [bx],
在没有寄存器参与的内存单元访问指令中,用 word ptr或byte ptr显性地指明所要访问的内存单元的长度是很必要的,否则,CPU无法得知所要访问的单元是字单元,还是字节单元
编程修改姚明的数据
2001年数据 | 2002年数据 |
---|---|
'Yao' | 'Yao' |
'19800912' | '19800912' |
15 | 11 |
32 | 13 |
‘SHH' | 'HOU' |
汇编
assume cs:code,ds:data
data segment
db 'Yao'
db '19800912'
dw 15
dw 32
db 'SHH'
data ends
code segment
start:
mov ax,data
mov ds ax
mov bx,0 ;从0开始改变数据
mov word ptr [bx+11],11
mov word ptr [bx+13],13
mov si,0
mov byte ptr [bx+15+si],'H'
inc si
mov byte ptr [bx+15+si],'O'
inc si
mov byte ptr [bx+15+si],'U'
mov ax,4c00h
int 21h
code ends
end start
C语言
#incldue
struct player
{
char name[3];
char birthday[9];
int num;//球衣号
int ppg;//point per game 场均得分
char team[3];//所在队名
}
struct player yao={"Yao",'19800912',15,32,'SHH'};
int main()
{
int i=0;
//开始修改数据
yao.num=11;
yao.ppg=13;
yao.team[i]='H';
i++;
yao.team[i]='O';
i++;
yao.team[i]='U';
return 0;
}
/*
yao.team[i]
1.yao是一个变量名,指明了结构体变量的地址
2.team是一个名称,指明了数据项team的地址
3.i是用来定位team中的字符
对汇编来说:
1.用bx基地址寄存器,定位整个结构体
2.用idata定位结构体中的某一个数据项
3.用si定位数据项中的元素
*/
指令格式:
1.div 寄存器
2.div 内存单元
数据存放位置
1.被除数:(默认)放在AX 或 (DX和AX)中 ;
2.除数:8位或16位,在寄存器或内存单元中
数据 | 存放位置(除数为8位) | 存放位置(除数为16位) |
---|---|---|
被除数 | ax | dx和ax |
除数 | 8位内存或寄存器 | 16位内存或寄存器 |
商 | al | ax |
余数 | ah | dx |
切记提前在默认的寄存器中设置好被除数,且默认寄存器不作别的用处。
//除法的位数示例: 6879H÷A2H:商A5,余FH 12345678H÷2EF7H:商633AH,余2D82H
示例程序 | 被除数 | 除数 | 商 | 余数 |
---|---|---|---|---|
div bl(8位) | ax | bl | al | ah |
div byte ptr ds:[0] | ax | ds*16+0 | al | ah |
div bx(16位) | dx*10000h+ax | bx | ax | dx |
div word ptr es:[0] | dx*10000h+ax | es*16+0 | ax | dx |
问题1:计算100001/100
100001d=186A1h,100d=64h,需要16位的除法
mov dx,1 //dx存放高位数据 mov ax,86a1 //ax存放低位数据 mov bx,64 //除数 div bx
问题2:计算1001/100
1001d=3e9h,100d=64h,进行8位除法
mov ax.3e9 //ax中存放被除数 mov bl,64 //bl中存放除数 div bl
用div 计算data段中第一个数据除以第二个数据后的结果,商存放在第3个数据的存储单元中
assume cs:code,ds:data
data segment
dd 100001 //4个字节
dw 100 //2个字节
dw 0 //2个字节
data ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,ds:[0] //将前两个字节的数据读取到ax中
mov dx,ds:[2] //将后两个字节的数据读取到dx中
div word ptr ds:[4] //再将100作为字单元数据读出,进行16位的除法
mov ds:[6],ax //再将结果保存到指定位置
mov ax,4c00h
int 21h
code ends
end start
功能:dup和db、dw、dd 等数据定义伪指令配合使用,用来进行数据的重复
1.db 3 dup (0) :定义了3个字节,它们的值都是0,相当于 db 0,0,0
2.db 3 dup (0,1,2) :定义了9个字节,由0、1、2重复3次构成,相当于db 0,1,2,0,1,2,0,1,2
3.db 3 dup (‘abc’ , ’ABC’): 定义了18个字节,构成'abcABCabcABCabcABC' ,相当于db ‘abcABCabcABCabcABC’