汇编程序中,用 '……' 的方式指明数据是以字符的形式给出的,编译器将 把它们转化为相对应的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’