汇编入门笔记(七)

0x19:[bx+idata] 处理数组

[bx+idata]表示一个内存单元, 例如:mov ax, [bx+200]

它表示将 ds:(bx)+200 内存单元的内容送入 ax 寄存器。

该指令也可以写成如下格式:

1
2
3
mov ax, [200+bx]
mov ax, 200[bx]
mov ax, [bx].200

利用数组改进大小写转换程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
assume cs:codesg,ds:datasg 

datasg segment
db 'BaSiC';转为大写
db 'MinIx';转为小写
datasg ends

codesg segment
start: mov ax,datasg
mov ds,ax

mov cx,5
mov bx,0
s: mov al,0[bx]
and al,11011111b ;'BaSiC';转为大写
mov 0[bx],al

mov al,5[bx]
or al,00100000b ;'MinIx';转为小写
mov 5[bx],al
inc bx
loop s

mov ax,4c00h
int 21h

codesg ends

end start

image-20210927212338335

0x20:SI 和 DI 寄存器

si和di是8086CPU中和bx功能相近的寄存器,si和di不能够分成两个8位寄存器来使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs: codesg, ds: datasg 

datasg segment
db 'welcome to masm!';用si和di实现将字符串‘welcome to masm!"复制到它后面的数据区中。
db '................'
datasg ends

codesg segment
start: mov ax, datasg
mov ds, ax
mov si, 0

mov cx, 8 ;只需要复制8次,si是16位寄存器,一次复制2字节
s: mov ax, 0[si] ;[0 + si]
mov 16[si], ax ;[16 + si] 使用[bx +idata]方式代替di,使程序更简洁
add si, 2 ; si只能作为16位寄存器
loop s

mov ax, 4c00h
int 21h
codesg ends
end start

[bx + si] 和 [bx + di]

[bx+si]和[bx+di]的含义相似

[bx+si]表示一个内存单元,它的偏移地址为(bx)+(si)

指令mov ax, [bx + si]的含义:将一个内存单元字数据的内容送入ax,段地址在ds中

该指令也可以写成如下格式:mov ax, [bx][si]

[bx+si+idata]和[bx+di+idata]

[bx+si+idata]表示一个内存单元,它的偏移地址为(bx)+(si)+idata

指令mov ax,[bx+si+idata]的含义:将一个内存单元字数据的内容送入ax,段地址在ds中

0x21:不同的寻址方式的灵活应用

  • [idata]用一个常量来表示地址,可用于直接定位一个内存单元;
  • [bx]用一个变量来表示内存地址,可用于间接定位一个内存单元;
  • [bx+idata]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元
  • [bx+si]用两个变量表示地址;
  • [bx+si+idata]用两个变量和一个常量表示地址。

image-20210928102822347

由于每个单词都是 16 个字节,所以利用二重循环来处理。

image-20210928102914058

实际上,这个程序是有问题的。

因为我们进行二重循环时,只有 cx 一个1循环计数器,当进行内层循环时,会覆盖外层循环次数。

第一次改进:

image-20210928103134897

我们利用 dx 寄存器来暂存外层循环时 cx 的值,当内层循环结束时,再从 dx 中恢复 cx 的值。

但是寄存器的数量有限,当程序复杂时,寄存器数量将不够用。

第二次改进:利用栈来暂存数据!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

;将datasg段中每个单词改为大写字母
assume cs:codesg,ds:datasg,ss:stacksg

datasg segment
db 'ibm ' ;16
db 'dec '
db 'dos '
db 'vax ' ;看成二维数组
datasg ends

stacksg segment ;定义一个段,用来做栈段,容量为16个字节
dw 0, 0, 0, 0, 0, 0, 0, 0
stacksg ends

codesg segment
start: mov ax, stacksg
mov ss, ax
mov sp, 16
mov ax, datasg
mov ds, ax
mov bx, 0 ;初始ds:bx

;cx为默认循环计数器,二重循环只有一个计数器,所以外层循环先保存cx值,再恢复,我们采用栈保存
mov cx, 4
s0: push cx ;将外层循环的cx值入栈
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 ;下一行
pop cx ;恢复cx值
loop s0 ;外层循环的loop指令将cx中的计数值减1

mov ax,4c00H
int 21H
codesg ends
end start

0x22:寄存器小结

image-20210928111119420

bx、si、di和bp

在8086CPU中,只有这4个寄存器可以用在“[…]”中来进行内存单元的寻址。

在[ ]中,这4个寄存器可以单个出现,或只能以4种组合出现:bx和si、bx和dibp和si、bp和di

只要在[……]中使用寄存器bp,而指令中没有显性地给出段地址, 段地址就默认在ss中。

同样地,使用寄存器bx,没有显式给出段地址,段地址默认为ds。

坚持原创技术分享,您的支持将鼓励我继续创作!