灵活定位内存地址,灵活应用
1.0 and和or指令
-
and指令,逻辑运算 &按位与运算 都真为真
-
mov al, 0110 0011B and al, 0011 1011B 执行后al=0010 0011B
-
通过该指令可以将操作对象的相对位设置为0
-
mov al, 1111 1111B and al, 1011 1111B 执行后al=1011 1111B 将操作对象的第6位设置为0
-
or指令,逻辑运算 | 按位或运算 都假为假
-
mov al, 0110 0011B or al, 0011 1011B 执行后al=0111 1011B
-
通过该指令可以将操作对象的相对位设置为1
-
mov al, 0000 0000B or al, 0100 0000B 执行后al=0100 0000B 将操作对象的第6位设置为1
1.1 ASCII
1.2 以字符的形式给出数据集
-
我们可以在编程时用‘ ...... ‘的方式指定数据是以字符的形式给出的,编译器将他们转换为ASCII码
-
db 'linux' db 'unix'
1.3 大小写转换的问题
-
通过对比,我们可以看出小写字母的ASCII码的值比大写字母的大20H,十进制也就是32
-
可以看出除第五位外,大小写字母的其他各位都是一样的
-
一个字母,我们不管它之前是不是大写或者小写,我们将他的第5位置改变成0那它就变成大写字母,改为1就是小写
-
assume cs:code,ds:data data segment db 'LINUX' db 'UNIX' data ends code segment start: mov ax, data mov ds, ax mov bx, 0 mov cx, 5 s:mov al, [bx] or al, 00100000B mov [bx], al inc bx loop s mov ax, 4c00H int 21H code ends end start
1.4 [bx+idata] [bx+SI+idata] [bx+DI+idata]
-
为了更好更方便多的访问内存可以用 [bx+idata] 用bx加上一个常量来进行寻址
-
[bx+idata]的方式进行数组的处理
-
数组是在内存中一连串的区域,这个区域都是同一类型的变量
-
将上面程序用[bx+idata]修改,将’basic‘大写
-
assume cs:code,ds:data data segment db 'LINUX' db 'basic' data ends code segment start: mov ax, data mov ds, ax mov bx, 0 mov cx, 5 s:mov al, [bx] or al, 00100000B mov [bx], al mov al, [bx+5] and al, 11011111B mov [bx+5], al inc bx loop s mov ax, 4c00H int 21H code ends end start
-
[bx+idata]的方式为高级语言实现数组提供了变利的条件
1.5 SI和DI
-
SI和DI和bx寄存器的功能是相近的,但是他们不能拆成两个8位的寄存器
-
以下s三组代码实现了相同的功能
-
mov bx, 0 mov ax, [bx] mov si,0 mov ax, [si] mov di,0 mov ax, [di]
-
由SI和DI 实现将字符串”helloworld“复制到它后面的数据区中 看代码
-
SI指向原始字符串,DI指向复制的空间,然后循环完成复制
-
assume cs:code,ds:data data segment db 'helloworld' db '..........' data ends code segment start: mov ax, data mov ds, ax mov si, 0 mov di, 10 mov cx, 5 f: mov ax, [si] mov [di], ax add si, 2 add di, 2 loop f mov ax, 4c00H int 21H code ends end start
-
用[bx+idata] 简化程序
-
assume cs:code,ds:data data segment db 'helloworld' db '..........' data ends code segment start: mov ax, data mov ds, ax mov si, 0 mov cx, 5 f: mov ax, [si] mov [si+10], ax add si, 2 loop f mov ax, 4c00H int 21H code ends end start
1.6 编程题
-
1.实现定义字符的大写首字母
-
assume cs:code,ds:data data segment db '1. edit ' db '2. file ' db '3. view ' db '4. help ' data ends code segment start: mov ax, data mov ds, ax mov bx, 0 mov cx, 4 s: mov al, [bx+3] and al, 11011111B mov [bx+3], al add bx, 10 loop s mov ax, 4c00H int 21H code ends end start
-
2.实现将字符全部大写,我们需要进行4*3次的双重循环
-
assume cs:code,ds:data,ss:stack data segment db '1. edit ' db '2. file ' db '3. view ' db '4. help ' data ends stack segment dw 0,0,0,0,0,0,0,0 ;定义栈区,用来存放cx循环的值 16个字节 stack ends code segment start: mov ax, stack mov ss, ax mov sp, 16 mov ax, data mov ds, ax mov bx, 0 mov cx, 4 ;定义外层循环 s0: push cx ;将cx的值进行压栈 mov si, 3 mov cx, 4 ;定义内层循环 s: mov al, [bx+si] and al, 11011111B mov [bx+si], al inc si loop s add bx,10 pop cx ;从栈顶弹出原来cx的值 loop s0 ;外层循环将cx减一 mov ax, 4c00H int 21H code ends end start
1.7 word ptr
-
下面的指令中,用word ptr指明了指令访问内存单元是一个字单元数据
-
mov word ptr ds:[0], 1 add word ptr ds:[0], 1 inc word ptr [bx] 下面的指令中,用word ptr指明了指令访问内存单元是一个字节单元数据 mov byte ptr ds:[0], 1 ;
-
在没有寄存器参与的内存单元访问指令中,用word ptr byte ptr显性的指出所要访问内存单元的长度是很有必要的
-
否则CPU无法得知所要访问的是字单元,还是字节单元
1.8 寻址方式的综合应用
- 一般来说,我们用[bx+idata+si] 的方式来访问结构体中的数据
- 用bx定义结构体,idata定义结构体中的某一个数据项,用si定位数据项中单个元素
1.9 div指令
-
division 是除法指令 注意事项
-
除数:8位或者16位在寄存器中或者在内存单元中
-
被除数:默认放在AX 或DX和AX中
-
除数是8位的话 商存储在al中 余数存储到ah中
除数是16位的话 商存储在ax中 余数存储到dx中 -
计算10/3
-
assume cs:code,ds:data data segment db 3 ;定义除数 data ends code segment start: mov ax, data mov ds, ax mov ax, 10 ;定义被除数 mov bx, 0 div byte ptr [bx] ;指向定义的除数 mov ax, 4c00H int 21H ;除数是8位的话 商存储在al中 余数存储到ah中 ;除数是16位的话 商存储在ax中 余数存储到dx中 code ends end start
-
如果除数是16位的话
-
assume cs:code,ds:data data segment db 3 data ends code segment start: mov ax, data mov ds, ax mov ax, 1 ;0000 0000 0000 0001 mov dx, 1 ;0000 0000 0000 0001 mov bx, 0 div word ptr [bx] ;这里就是dx在高位 ax在地位 0000 0000 0000 0001 0000 0000 0000 0001 ;十进制65537/3 ;商存在AX中 ;余数存储到DX中 mov ax, 4c00H int 21H code ends end start
-
如果除数是16位的话 被除数是32位,高16位在dx中,低16位在ax中,如上代码
2.0 伪指令 DD dd
-
dd是用来定义dword(double word)双字型数据 32个字节
-
dd 31415926 ;占32个字节 正常定义即可
2.1 dup duplicate 复制
-
dup是一个操作符,在汇编语言中同db dw dd一样,是由编译器处理的符号
-
它是配合db dw dd 符号使用的,用来数据的重复
-
db 3 dup(0) ;定义三个字节他们的值都是0 db 0,0,0 ;相当于这样 db 3 dup(0,1,2) ;定义九个个字节他们的值是0,1,2,0,1,2,0,1,2 db 0,1,2,0,1,2,0,1,2 ;相当于这样 assume cs:code,ds:data data segment db 2 dup('helloworld') ;连续定义两个以字节为单位的helloworld data ends code segment start: mov ax, data mov ds, ax mov ax, 4c00H int 21H code ends end start
总结 实验
- 将data中数据按格式存入table段中
- 可以用循环来减少代码
assume cs:code,ds:data,es:table,ss:stack
data segment
db '1990','1991','1992','1993','1994','1995','1996','1997','1998','1999','2000'
db '2001','2002','2003','2004','2005','2006','2007','2008','2009','2010' ;最近21年的年份
;计算年份的偏移地址 4*21=84 十六进制54H用53H
dd 116,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,5840 ;21年每年的收入
;计算收入的偏移地址 54H+54H=A8H 用A7H
dw 1,6,9,12,113,6,9,12,113,6,9,12,113,6,9,12,113,6,9,12,50 ;21年每年的人数
;计算人数的偏移地址 A8H+2A=D2 用D1
data ends
table segment
db 21 dup('year summ ne !! ')
table ends
stack segment
dw 8 dup('0')
stack ends
code segment
start: mov ax, data
mov ds, ax ;将ds和data挂钩
mov ax, table
mov es, ax ;将es和table挂钩
mov bx, 0 ;初始化参数
mov si, 0
mov di, 0
mov cx, 21 ;设置循环次数21次
s: mov al, ds:[bx] ;存放年份
mov es:[di], al
mov al, ds:[bx+1]
mov es:[di+1], al
mov al, ds:[bx+2]
mov es:[di+2], al
mov al, ds:[bx+3]
mov es:[di+3], al
mov ax, [bx+54H] ;存放收入
mov dx, [bx+56H]
mov es:[di+5H], ax
mov es:[di+7H], dx
mov ax, [si+0A8H] ;存放人数
mov es:[di+0AH], ax
mov ax, [bx+54H] ;存放人均收入
mov dx, [bx+56H]
div word ptr ds:[si+0A8H]
mov es:[di+0dH], ax
add bx, 4
add di, 16
add si, 2
loop s
mov ax, 4c00H
int 21H
code ends
end start