1.and 和 or 指令

1.and指令:逻辑与指令,按位进行与运算 【一假即假】

1
2
3
4
5
6
按位与就是按位与咯
01=0;10=0;11=1;00=0
比如:
mov al,01100011B
and al,00111011B
执行后 al=00100011B

功能:通过该指令可以将操作对象的相应位设置成0

1
2
3
4
5
比如:
将al中的第6位设为0
and al,10111111B
将第0位设为0
and al,11111110B

2.or指令:逻辑或,按位进行或运算【一真即真】

1
2
3
4
比如:
mov al,01100011B
or al,00111011B
执行后: al=01111011B

功能:通过该指令可将操作对象的相应位设为1,其他位不变

1
2
3
比如:
al的第6位设置位1
or al,01000000B

2.关于ASCII码

没什么…

3.以字符形式给处的数据

1.在汇编中,我们可以用 ‘ xxx ‘ 的方式指明数据是以字符的形式给出的,编译器将把它们转化为相对应的ASCII码

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
assume ds:data
data segment
db 'unIX' ;这里会把u n I X 会转化成ASCII码,然后ASCII码只需要8为,所以用db
db 'foRK'
data ends

code segemnt
start: mov al,'a' ;这里ascii码就是是 MOV AL,61H
mov vl,'b'
mov ax,4c00H
int 21h

code ends
end satrt

4.ASCII码大小也转化

改变字母的大小写,其实就是改写ASCII码的值

十进制:a 97 A65 相差32

十六进制 :a61 41A 相差20h

其实在二进制的时候,也就是在第五位0改成1

所以小写<–>大写 可以用and 和 or 完成大小写的转变

哈哈哈练个手:

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
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'iNfOrMaTiOn'
datasg ends

codesg segment
start: mov ax,datasg
mov ds,ax ;设置ds指向datasg段

mov bx,0 ;设置(bx)=0,将ds:bx指向'BaSiC'中的第一个字母

mov cx,5 ;设置循环5次,因为BaSiC有五个字母
s: mov al,[bx] ;将ASCII码从ds:bx所指向的内存单元中取出一个放入al中
and al,11011111B;将dl中ASCII码的第五位转成0,将其变成大写字母
mov [bx],al ;将转化有的ASCII码又放回去
inc bx ;(bx)++,将ds:bx指向下一个字母
loop s

mov bx,5

mov cx,11
s0: mov al,[bx] ;
or al,00100000B ;这里是把字母全部转化成小写
mov [bx],al
inc bx
loop s0

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

5.[bx+idata]

1.在前面我们可以用[bx]的方式来指明一个内存单元,我们还可以用一种更为灵活的方式来搞

1
2
[bx+idata]
这个就相当于是:偏移地址=(bx)+idata

2.变形

1
2
3
4
[bx+idata]
idata[bx]
[bx].idata
这三种方式都可以

6.用[bx+idata]的方式进行数组的处理

我们观察datasg段中的俩个字符串,一个起始地址为0,另外一个是5

我们就可以用[0+bx]和[5+bx]在同一个循环中定位这俩个字符串中的字符

1
在这里0和5给定了2个字符的起始偏移地址,bx中给出了从起始偏移地址开始的相对地址

所以现在就可以把上面的改进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    mov ax,datasg
mov ds,ax
mov bx,0

mov cx,5
s: mov al[bx]
and al,11011111B
mov [bx],al

mov al[5+bx]
or al,00100000B
mov [5+bx],al
inc bx
loop s

7.SI和DI

1.SI和DI是8086CPU中和bx功能相近的寄存器,但是SI和DI不能够分成8位寄存器来使用

问题:用寄存器SI和DI实现将字符串 ‘welcome to masm!’复制到它后面的数据区中

思路:用ds:si指向要复制的原始位置,用ds:di来指向复制的位置

写代码咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
codesg segment
start: mov ax,datasg
mov ds,ax
mov si,0
mov di,16

mov cx,8
s: mov ax,[si]
mov [di],ax
add si,2 ;因为si 和di 都是16位所以+2
add di,2
loop s

mov ax,4c00H
int 21h
codesg ends
ends start

我们也可以用数组的思想

1
2
3
4
5
6
7
8
9
10
11
codesg segment
start: mov ax,datasg
mov ds,ax
mov si,0
mov cx,8
s: mov ax,0[si]
mov 16[si],ax
add si,2
loop s


8.[bx+si]和[bx+di]

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

2.看看

1
2
3
mov ax,[bx+si]
将一个内存单元的内容送入ax中,这个内存单元的长度为2【字单元】
偏移地址是:bx中的数值+si中的数值,段地址默认是ds

3.同类

1
2
3
[bx+si]
[bx][si]
这俩种其实是同一种

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

emmm和上面的雷同,我吐了

10.用不同的寻址方式的灵活应用

1.比较一下之前用到的几种定位内存地址的方法(也就是寻址的方式)

1
2
3
4
5
1.[idata]用一个常量来表示一个地址,可以用于之间定位一个内存单元
2.[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元
3.[bx+idata]这个感觉就是一个一维数组idata[bx]和a[]可以雷同。用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元
4.[bx+si]用2个变量来表示地址
5.[bx+si+idata]用两个变量和一个常量来表示地址

2.EMMM这里听得太入迷了,忘了写笔记

无所谓了,那就不写了,记录一点有用的就好了

题目:

image-20230411225142572

3.我们在进行一次循环的时候使用CX 会递减,这个是没有问题的

但是如果我们要用循环里面套循环,这个时候,在里面的循环又需要调用一次cx,这样就会形成cx被覆盖!!

1
2
3
4
为了解决这个问题
我们就应该每次开始内层循环的时候,将外层循环的cx中的数值保存起来,在执行外层循环的loop指令前,再回复外层循环的cx数值

我们可以用寄存器dx来保存cx中的值

改进后的循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
codesg segment
start: mov ax,datasg
mov ds,ax
mov bx,0 ;用bx来定位行

mov cx,4
s0: mov dx,cx ;用dx来保存外层的cx中的值
mov si,0

mov cx,3
s: mov al,[bx+si]
and al,11011111B
mov [bx+si],al

inc si
loop s ;这个时候cx中的值已经变成0了

add bx,16
mov cx,dx ;将外层的cx还原
loop s0 ;cx=cx-1 再判断是否为0是否再循环

上面的程序中就是用的dx,但是如果dx被使用了,或者说找不到合适的寄存器怎么办呢?

就是说,我们这些数据就是需要展示存放一下

为了寻找一个通用的方案,所以我们就可以用内层,我们就可以将需要暂时存的数据放到一段内存单元中,需要使用的时候,再从内存单元中恢复,这样我们就需要开辟一段内存空间

改进后的代码

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
datasg segment
db 'ibm.............'
db 'dec.............'
db 'dos.............'
db 'vax.............'
dw 0 ;这里定义一个字,开辟一个字空间来保存cx
datasg ends

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

mov bx,0

mov cx,4
s0: mov ds:[40H],cx ;将外层循环的cx值保存在datasg:40H单元中
mov si,0 ;用si来表示列

mov cx,3 ;设置内存循环次数
s: mov al,11011111B
and al,11011111B
mov [bx+si],al ;[bx+si]bx就是行si就是列,虽然在内存中是线性排列的,但是我们可以这么理解
inc si
loop s

add bx,16 ;这里用bx来表示行
mov cx,ds:[40H]
loop s0

mobv ax,4c00H
int 21h
codesg ends
end start

但是,这样的作法有点麻烦

所以我们又进步了!!用栈

push 后 pop

又来写代码了

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
datasg segment
db 'ibm.............'
db 'dec.............'
db 'dos.............'
db 'vax.............'
datasg ends

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

codesg segment
start: mov ax,stacksg
mov ss,ax
mov sp,16

mov ax,datasg
mov ds,ax

mov bx,0

mov cx,4
s0: push cx ;把外层循环的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的时候,在把他pop出来
loop s0

mov ax,4c00H
int 21h

codesg ends
end start

7.9

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
43
44
45
46
;将datasg段中的每个单词的前4个字母改成大写字母
assume cs:codesg,ss:stacksg,ds:datasg

stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends

datasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg ends
;[bx+si+idata][bx];当列;[si+idata]当行数
codesg segment
start: mov ax,stacksg
mov ss,ax
mov sp,16 ;等会儿外层循环需要push的栈

mov ax,datasg
mov ds,dx ;确定数据的段地址

mov bx,0

mov cx,4 ;外层需要4行也就是4次循环
s: push cx ;把cx保护起来

mov si,0 ;因为每一次循环的时候,每一列都行重新索引字母的位置,所以si需要设置在外面

mov cx,4 ;把后面的4个字母都需要改成大写所以要循环4次
s0: mov dl,ds:[bx+3+si] ;这样索引住第一个字母,通过si的自增来索引后面的4个字母
mov dl,11011111B ;这样就是改成大写
mov ds:[bx+3+si],dl ;这里需要把dl中的值放回原来的内存中
inc si ;si++索引下一个
loop s0

add bx,16 ;选中下一行
pop cx ;把原来的cx还回来
loop s

mov ax,4c00H
int 21h

codesg ends
end start

总结一下啦

1.si di

1
这两个寄存器和ax一样,但是不能被拆开
1
2
主要用于:
[bx+si+idata]

2.用栈来保护数据

1
2
push cx
pop cx