1. and和or指令
- and指令,逻辑与指令,and al, 11110111B,可将操作对象的相应位设为0,其它位不变。
- or指令,逻辑或指令,or al, 00001100B,可将操作对象的相应位设为1,其它位不变。
2. 以字符形式给出的数据
我们可以在汇编程序中,用'.......'的方式指明数据是以字符的形式给出的,编译器把它们转化为相对应的ASCII码。如下程序:
sql
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
因'DS = 075CH',所以程序从076C段开始,而data段是程序中第一个段,它就在程序的起始处,所以它的段地址为076C
3. 大小写转换问题
将datasg中的第一个字符串转化为大写,第二个字符串转化为小写。
arduino
datasg segment
db 'Basic'
db 'iNfOrMaTiOn'
datasg ends
以字符'a'为例,它的ASCII码是61H,大写字符'A'的ASCII码为41H。我们可知大写字符的ASCII码 + 20H = 小写字符的ASCII码。但前提是我们先判断这个字符是大写还是小写。从另外一个角度出发,小写字符的ASCII码的二进制编码的第六位总是1。
大写 二进制 小写 二进制
A 01000001 a 01100001
我们可以利用逻辑与或指令改变它的编码,这样就无需判断它原本是大写或者小写。
arduino
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; 设置bx = 0,ds:bx指向'BaSiC'第一个字母
mov cx, 5; 循环五次
s0: mov al, [bx]
and al, 11011111B
mov [bx], al
inc bx
loop s0
mov bx, 5
mov cx, 11
s1: mov al, [bx]
or al, 00100000B
mov [bx], al
inc bx
loop s1
mov ax, 4c00H
int 21H
code ends
end start
4. [bx + idata]
在前面,我们用[bx]的方式来指明一个内存单元,还可以用一种更为灵活的方式来指明内存单元: [bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata。
有了[bx+idata]这种表示内存单元的方式,我们就可以用更高级的结构来看待所要处理的数据。在codesg中填写代码,将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写。问题如下:
kotlin
assume cs:code, ds:data
data segment
db 'BaSiC'
db 'MinIX'
data ends
code segment
start:
code ends
end start
按照原来的方法,用[bx]的方式定位字符串中的字符。代码段中的程序如下。
ini
code segment
start: mov ax, data
mov ds, ax
mov bx, 0; 设置bx = 0,ds:bx指向'BaSiC'第一个字母
mov cx, 5; 循环五次
s0: mov al, [bx]
and al, 11011111B
mov [bx], al
inc bx
loop s0
mov bx, 5
mov cx, 5
s1: mov al, [bx]
or al, 00100000B
mov [bx], al
inc bx
loop s1
mov ax, 4c00H
int 21H
code ends
观察以上程序可知,两个字符串的起始地址不一样,一个起始地址是0,另一个起始地址是5。我们可以将字符串看作两个数组,一个从0地址开始存放,另一个从5开始存放。那么,我们可以用[0+bx]和[5+bx]的方式在同一个循环里定位这两个字符串的字符。
less
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]
mov ah, [5+bx]
and al, 11011111B
or ah, 00100000B
mov [bx], al
mov [5+bx], ah
inc bx
loop s
mov ax, 4c00H
int 21H
code ends
end start
5. SI和DI
SI和DI是8086CPU中和bx中功能相近的寄存器,但是它不能够分成两个8位寄存器使用。
问题:用SI和DI实现将字符串'welcome to masm!'复制到它后面的数据区中。
sql
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; 可以按字来复制,减少循环次数
s: mov ax, [si]
mov [16+si], ax
add si, 2
loop s
mov ax, 4c00H
int 21H
code ends
end start
6. 不同的寻址方式的灵活应用
-
idata\]用一个常量来表示地址,可用于直接定位一个内存单元。
- [bx+idata]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元。
-
bx + si\]用两个变量来定位内存单元。
css
mov ax, [200+bx+si]
mov ax, [bx+200+si]
mov ax, 200[bx][si]
mov ax, [bx].200[si]
mov ax, [bx][si].200
将data段中的每个单词的头一个字母改为大写字母。
kotlin
data segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
sql
assume cs:code, ds:data
data segment
db '1. file ';每个字符串的长度都是16个字节
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段中每个单词改为大写字母
kotlin
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
每个字符串的长度都是16个字符,这里很明显有两个循环变量,一个用来定位行,一个用来定位列。
ini
mov ax, data
mov ds, ax
mov bx, 0; 用bx来定位行
mov cx, 4
s0: mov dx, cx; 将外层循环的cx值保存在dx中
mov si, 0
mov cx, 3; cx设置内层循环的次数
s: mov al, [bx+si]
and al, 11011111B
mov [bx+si], al
inc si
loop s
add bx, 16
mov cx, dx; 用dx中的值恢复外层循环的计数
loop s0
但寄存器的数量毕竟是有限的,有的时候会出现寄存器不够的情况,这个时候我们可以在再开辟一个内存来储存相应的计数值。但这样做毕竟比较繁琐。当我们需要保存多个数据的时候,我们必须记住数据放到了哪个单元,这样程序容易混乱。一般来说,在需要暂存数据的时候,我们都应该使用栈,从而使得我们的程序更加清晰。
arduino
assume cs:code, ds:data, ss:stack
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
stack segment
dw 0, 0, 0, 0, 0, 0, 0, 0
stack ends
code segment
start: mov ax, stack
mov ss, ax
mov sp, 50H
mov ax, data
mov ds, ax
mov bx, 0
mov cx, 4; 外循环4次
s0: push cx ;保存外循环次数
mov cx, 3
mov si, 0
s1: mov al, [bx+si]
and al, 11011111B; 切记不要把and写成add
mov [bx+si], al
inc si
loop s1
add bx, 16
pop cx
loop s0
mov ax, 4c00H
int 21H
code ends
end start
*将data段中每个单词的前4个字母改为大写字母。 *编写程序如下:
arduino
assume cs:code, ds:data, ss:stack
data segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
data ends
stack segment
dw 0, 0, 0, 0, 0, 0, 0, 0
stack ends
code segment
start: mov ax, stack
mov ss, ax
mov sp, 50H
mov ax, data
mov ds, ax
mov bx, 0
mov cx, 4; 外循环4次
s0: push cx ;保存外循环次数
mov cx, 4
mov si, 3
s1: mov al, [bx+si]
and al, 11011111B
mov [bx+si], al
inc si
loop s1
add bx, 16
pop cx
loop s0
mov ax, 4c00H
int 21H
code ends
end start
本章总结:
- 不同寻址方式的应用和意义
- 二重循环问题的处理
- 栈的应用
- 大小写转化的方法
- and、or指令
7. bx、si、di和bp
- 只有这四个寄存器可以用在"[....]"中进行内存单元的寻址。
- 这四种寄存器可以单独出现,或只能以四种组合方式出现,如[bx+si],[bx+di],[bp+si],[bp+di]。
- 只要在[...]中使用寄存器bp,而指令中没有显性地给出段地址,段地址就默认在ss中。
8. 机器指令处理的数据在什么地方
指令在执行前,所要处理的数据可以在3个地方:CPU内部(包括寄存器和指令缓冲器)、内存、端口。
9. 指令处理的数据有多长
8086CPU可以处理两种尺寸的数据,btye和word。所以在机器指令中要指明进行的是字操作还是字节操作。
- 通过寄存器名指明要处理的数据的尺寸。
- 在没有寄存器名的情况下,用操作符X ptr指明内存单元的长度,这里的X可以为word和byte。
下面的指令中,用word ptr指令了指令访问的内存单元是一个字单元。
arduino
mov word ptr ds:[0], 1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx], 2
下面的指令中,用byte ptr指令了指令访问的内存单元是一个字节单元。
csharp
mov byte ptr ds:[0], 1
inc byte ptr [bx]
inc byte ptr ds:[0]
add byte ptr [bx], 2
在没有寄存器参与的内存单元访问指令中,用word ptr和byte ptr显性地指令所要访问的内存单元的长度是很必要的。
- 有些指令默认了访问的是字单元还是字节单元,比如,push [1000H] 就不用指明访问的是字单元还是字节单元,因为push指令只进行字操作。
10. 寻址方式的综合应用
关于DEC公司的一条记录如下。
公司名称: DEC
总裁姓名: Ken Olsen
排名: 137
收入: 40
著名产品: PDP
它在内存中的位置如表所示:
在内存中起始地址 | 存放数据 |
---|---|
seg:60 +00 | 'DEC' |
+03 | 'Ken Oslen' |
+0C | 137 |
+0E | 40 |
+10 | 'PDP' |
当我们需要修改它的数据如下:
排名: 38
收入: 增加70
著名产品:'VAX'
arduino
mov ax, seg
mov ds, ax
mov bx, 60H ;确定记录地址,ds:bx
mov word ptr [bx+0cH], 38
add word ptr [bx+0eH], 70
mov si, 0 ;用si来定位产品字符串
mov byte ptr [bx+10H+si], 'V'
inc si
mov byte ptr [bx+10H+si], 'A'
inc si
mov byte ptr [bx+10H+si], 'X'
11. div指令
div是除法指令,格式:div reg/ div 内存。并且它是单操作数指令,被除数默认在寄存器AX或AX与DX中。
如果除数是8位,被除数则为16位,默认在AX中存放;
如果除数是16位,被除数则为32位,默认在DX和AX中存放,DX存放高位,AX存放低位。
结果:如果除数是8位,则AL存储除法操作的商,AH存储除法操作的余数;如果除数是16位,则AX存储除法操作的商,DX存储除法操作的余数。
问题:利用除法指令计算100001/100的值
因为100001大于65535,所以它被除数是32位,100001转为16进制为186A1H
css
mov dx, 1
mov ax, 86A1H
mov bx, 100
div bx
12. 伪指令dd和dup
db和dw分别用来定义字节型数据和字型数据。dd用来定义双字节数据。比如:
kotlin
data segment
db 1
dw 1
dd 1
data ends
上述语句定义了3个数据:
第一个数据为01H,
第二个数据为0001H,
第三个数据为00000001H。
dup是一个操作符,在汇编语言中通db、dw、dd一样,也是由编译器识别处理的符号。用来进行数据的重复。比如
scss
db 3 dup (0)
定义了三个字节,值都是0,相当于db 0, 0, 0
scss
db 3 dup (0, 1, 2)
定义了9个字节,相当于db 0, 1, 2, 0, 1, 2, 0, 1, 2
实验七 寻址方式在结构化数据访问中的应用
kotlin
assume cs:code, ds:data, ss:table
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984'
db '1985','1986','1987','1988','1989','1990','1991','1992','1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
table segment
db 21 dup ('year summ ne ?? ')
table ends
答案如下:
css
assume cs:code, ds:data, ss:table
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984'
db '1985','1986','1987','1988','1989','1990','1991','1992','1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
table segment
db 21 dup ('year summ ne ?? ')
table ends
code segment
start: mov ax, data
mov ds, ax
mov ax, table
mov ss, ax ;保存table段地址
mov bx, 0 ;定位data段中的年份和总收入数据
mov si, 0 ;定位data段中的雇员人数数据
mov bp, 0 ;定位table段行地址
mov cx, 21
s: mov ax, 00H[bx]
mov [bp], ax
mov ax, 00H[bx+2]
mov [bp+2], ax ;年份数据传送完毕
mov byte ptr [bp+4], ' ' ;添加空格
mov ax, 54H[bx]
mov dx, 54H[bx+2]
mov [bp+5], ax
mov [bp+7], dx ;总收入数据传送完毕
mov byte ptr [bp+9], ' ' ;添加空格
mov di, 0A8H[si]
mov [bp+0AH], di ;雇员人数添加完毕
mov byte ptr [bp+0CH], ' '; 添加空格
div di;
mov [bp+0DH], ax ; 添加收入数据
mov byte ptr [bp+0FH], ' '
add bx, 4
add si, 2
add bp, 10H
loop s
mov ax, 4c00H
int 21H
code ends
end start