《汇编语言(第四版)》---王爽 第七章更灵活的定位内存地址的方法 详细笔记+代码 ~后续章节笔记,课后检测,实验代码持续更新中

《汇编语言(第四版)》—王爽 第七章更灵活的定位内存地址的方法

第七章更灵活的定位内存地址的方法

7.1、and和or指令

and指令

逻辑与指令,按位进行与运算(两个都为1时结果才为1)

mov al,01100011B			al是一个8位的寄存器
and al,00111011B			al是一个8位的寄存器
执行后 al = 00100011B

and指令的功能:通过该指令可以将操作对象的对应位设为0,其他位不变------>原理:就是将对应位置的数字与0进行逻辑与运算,则该位一定会为0

例如:将al中的第五位设置为0    and al,11011111B
	 将al中的第零位设置为0    and al,11111110B
or指令

逻辑或指令,按位进行或运算(只要有1结果就为1)

mov al,01100011B			al是一个8位的寄存器
or al,00111011B				al是一个8位的寄存器
执行后 al = 01111011B

or指令的功能:通过该指令可以将操作对象的对应位设置为0,其他位不变------>原理:就是将对应位置的数字与1进行逻辑或运算,由于逻辑或运算中有1则结果为1,顾此时该位置一定为1

例如:将al中的第五位设置为1    or al,00100000B
 	 将al中的第零位设置为1    or al,00000001B

7.2、关于ASCII码

在这里插入图片描述
(上述的ASCII表是十进制请款下的表)

一个文件编辑过程中,就包含着按照ASSCII编码规则进行的编码和解码,例如在ASCII中,用61H表示"a",用62H表示"b"等等

7.3、以字符形式给出的数据

我们在编辑程序中,用’…'的方式指明数据是以字符形式给出的,编译器将把他们转化为相对应的ASCII码

assume cs:code,ds:data

data segment
	db 'unIX'	定义字节型数据一个字符占8位
	db 'foRK'
data ends

code segment
	start: mov al,'a'
		   mov bl,'b'
		   
		   mov ax,4c00H
		   int 21H
		   
code ends
end start

在这里插入图片描述

从指令可以看出ASCII码和内存单元的对应关系

7.4、大小写转换的问题

要改变一个字母的大小写,实际上就是改变它所对应的ASCII码

大写    二进制    十六进制   小写    二进制     十六进制
 A    01000001	  41H      a	01100001      61H
 B    01000010	  42H      b    01100010	  62H
 C	  01000011	  43H  	   c	01100011      63H
 D	  01000100	  44H 	   d	01100100      64H

观察上述的大小写二进制表达,可以发现大小写字母的二进制表达中只是第五位的0或者1的区别

我们可以将大写字母的ASCII码的值加上20H变为小写字母,同理小写字母变大写字母即为减少20H

提出问题:如何将数据段中的第一个字符串“BaSiC”中的小写字母变成大写,第二个字符串“iNfOrMaTiOn”中的大写字母变成小写	
assume cs:codesg , ds:datasg

data segment
	db 'BaSic'
	db 'iNfOrMaTiOn 'datasg ends
data ends

code segment
start:mov ax,data
	  mov ds,ax
	  mov bx,0
	  mov cx,5
   s:mov al,[bx]
	  如果(al)>61H,则为小写字母的ASCII码,则:sub al,20Hmov [bx],al
	inc bx
	loop s
	: 
	codesg ends
	end start

很显然判断将用到我们目前还没有学到的指令,那我们尝试着用已经学习到的知识来进行操作

思路:
由于上述分析可知大写字母和小写字母的二进制表示中只有第五位是不一样的,小写字母为1,而大写字母位0,那么进一步思考,如何将第五位的0或者是1,进行修改成为对应的值呐?
很显然and指令和or指令可以做到
因此我们可以用这两个指令来实现转换
一个字母无论它是小写还是大写只要第五位为0,它就必将为大写字母,第五个位置为1,它就必将为小写字母
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'有5个字母
		s: mov al,[bx]      ;将ASCI工码从ds:bx所指向的单元中取出
		   and al,11011111B ;将al中的ASCII码的第5位置为0,变为大写字母
		   mov [bx],al      ;将转变后的ASCII码写回原单元
		   inc bx           ;(bx)加1,ds:bx指向下一个字母
		   loop s
		   
		   mov bx,5         ;设置(bx)=5,ds : bx指向'iNfOrMaTion'的第一个字母
		   mov cx,11        ;设置循环次数11,因为'iNfOrMaTion'有11个字母
		s0:mov al,[bx]
		   or al,00100000B  ;将al中的ASCII码的第5位置为1,变为小写字母
		   mov [bx],al
		   inc bx
		   loop s0
		   
		   mov ax,4c00h
		   int 21h

codesg ends
end start		   

7.5、[bx+idata]

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

实例说明:mov ax,[bx+200]
含义:将一个内存单元的内容送到ax中,这个内存单元长度为两个字节,存放一个字,偏移地址为bx中的数值加上200,段地址在ds中
数字化描述:(ax) = ((ds)*16 + (bx)+200)

常用的表达方式有:

  • mov ax , [idata + bx]
  • mov ax , idata[bx]
  • mov ax , [bx].idata
课本问题7.1分析:
分析一:mov ax,[bx]是访问的字单元的段地址在ds中,即(ds)=2000H,偏移地址在bx中,(bx)=1000H
分析二:mov cx,[bx+1]访问的字单元在段地址ds中,(ds)=2000H;偏移地址为(bx)+1 = 1001H
分析三:add cx,[bx+2]访问的字单元在段地址ds中,(ds)=2000H;
偏移地址为(bx)+ 2 = 1002H

7.6、用[bx+idata]的方式进行数组的处理

在代码段中填写代码,将数据段中定义的第一个字符串的字母转化为大写,第二个字符串的字母转化为小写

改进前的代码:(省略了字符串的定义部分)
	mov ax,data 
	mov ds,ax

	mov bx,0
	mov cx,5
s:  mov al,[bx]
	and a1,11011111b
	mov [bx] , al
	inc bx
	loop s
	
	mov bx,5
	mov cx,5
s0: mov al,[bx]
	or al,00100000b
	mov [bx] ,al
	inc bx
loop s0
分析:我们可以将两个字符串看作为数组(两个字符串长度都为5,编号从0开始),
此时两个字符串的起始地址为[0+bx],[5+bx],bx表示的是在数组中的相对偏移地
	址,0和5是起始的地址
改进后完整代码:
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,[0+bx]
		   and al,11011111b
		   mov [0+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语言的表现形式为:

在这里插入图片描述
C语言表示定位的方式为 a[i],b[i]

汇编语言表示定位方式为 0[bx],5[bx]

通过比较发现,[bx+idata]的方式为高级语言实现数组提供了便利机制

7.7、SI和DI

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

1> mov bx,0   mov ax,[bx]
2> mov si,0   mov ax,[si]
3> mov di,0	  mov ax,[di]
这三个指令实现了相同的功能
结合上一小节所学到的指令
1> mov bx,0   mov ax,[bx+idata]
2> mov si,0   mov ax,[si+idata]
3> mov di,0	  mov ax,[di+idata]
这三个指令同样也实现了相同的功能

在这里插入图片描述

问题7.2分析:
我们编写的程序大都是进行数据的处理,而数据在内存中存放,所以我们在处理数据
之前首先要搞清楚数据存储在什么地方,也就是说数据的内存地址。
现在我们要对datasg段中的数据进行复制,先来看一下要复制的数据在什么地方,
datasg:0,这是要进行复制的数据的地址。那么复制到哪里去呢?它后面的数据区。
“welcome to masm!”从偏移地址0开始存放,长度为16个字节,所以,它后面的数
据区的偏移地址为16,就是字符串“.....”存放的空间。
清楚了地址之后,我们就可以进行处理了。我们用ds:si 指向要复制的源始字符串,
用ds:di指向复制的目的空间,然后用一个循环来完成复制。
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
           add di,2
           loop s
		   mov ax,4c00h
		   int 21h
codesg ends
end start
注意:因为si,di,bx均为16位的寄存器,则一次是传输两个字节

思考:如何使得代码段变得更加的简介

从上述的代码中可以看出,我们在赋值时用到了两个寄存器,程序较为复杂,且两个字符串的长度均为16,我们可以想到用数组的方式去实现,即为[bx(si或者di) + idata]的方法,此时idata为数组与数组之间的偏移地址,寄存器中的位数组中的相对偏移地址

优化后的代码:
codesg segment
	start: mov ax,datasg
	       mov ds,ax
		   mov si,0
		   mov cx,8
	s:     mov ax,o [si]
           mov 16[si],ax
           add si,2
		   loop s
		   mov ax,4c00h
		   int 21h
codesg ends
end start

7.8、[bx+si]和[bx+di]

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

mov ax,[bx+si]的数字化表示形式为 (ax) = ((ds) * 16 + (bx)+(si))

mov ax,[bx+si]的其他表示形式:mov ax,[bx] [si]

实例练习:

在这里插入图片描述

1> mov ax,[bx+si]
访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)=1000H;指令执行后(ax)=00BEH。
2> mov cx, [bx+si]
访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)=1001H;指令执行后(cx)=0600H。
3> add cx,[bx+di]
访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(di)=1002H;指令执行后(cx)=0606H。

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

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

[bx+si+idata]和[bx+di+idata]都表示内存单元

数字化的描述为(ax) = ((ds) * 16 + (si) + idata)

指令mov ax,[bx+si+idata]其他表示形式

  • mov ax,[bx+idata+si]
  • mov ax,[idata+bx+si]
  • mov ax,idata[bx] [si]
  • mov ax,[bx].idata[si]
  • mov ax,[bx] [si].idata

实例练习:

在这里插入图片描述

1> mov ax, [bx+2+si]
访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)+2=1002H;指令执行后(ax)=0006H。
2> mov cx,[bx+2+si]
访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)+2=1003H;指令执行后(cx)=6A00H。
3> mov bx,[bx+2+di]
访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(di)+2=1004H;指令执行后(bx)=226AH。

7.10、不同的寻址方式灵活使用

如果我们比较一下前面用到的几种定位内存地址的方法(可称为寻址方式),就可以发现:

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

下面我们将以具体的问题来体会CPU这样处理的用意

问题7.6:将数据段中的每个单词的头一个字母改写为大写字母

在这里插入图片描述
由图片分析可知:每个单词的第一个字母都在3的位置上,一共有六行数据,则此时列数是固定值,行数需要进行循环,所以此时需要的结构就是用[bx]表示行数,用idata表示列数3,即用到结构[bx+idata]

assume cs:codesg,ds:datasg

datasg segment
	db '1. file         '   ;定义为16位 空格补齐
	db '2. edit         '
	db '3. search       '
	db '4. view         '
	db '5. options      '
	db '6. help         '
datasg ends

codesg segment

	start: mov ax,datasg
	
	mov ds,ax
	mov bx,0
	
	mov cx,6
	s:mov al,[bx+3]			;注意:单位是字节,所以是al
	and al,11011111b		;使得第五位为0,则一定为大写
	mov [bx+3],al
	add bx,16
	loop s
	
	mov ax,4c00H
	int 21H
	
codesg ends
end start

问题7.7:将代码段中的每一个单词改为大写字母

在这里插入图片描述
由上图分析可知:我们需要将每个字母修改为大写字母,就相当于是需要一个4*3的循环(嵌套)

  • 外层循环按照行来进行
  • 内存按列来进行

大致思路如下:

R=第一行的地址;
mov cx, 4
s0 : c = 第一列的地址
mov cx, 3
s : 改变R行,c列的字母为大写
c=下一列的地址;
loop s
R = 下一行的地址
loop s0
我们用bx来做变量,定位每行的起始地址,用si来表示要修改的列,用[bx + si]的方式来对目标单元寻址

注意:如果按照上述文字表述编写指令并执行时就会出现错误,进入死循环------>原因是:问题就在于cx的使用,我们进行二重循环,却只用了一个循环计数器,造成了内存的时候覆盖了外层的循环计数值

在内层执行loop s完毕时cx的值为0,但是此时还需要执行外层的循环loop s0使得cx的值为(cx) = (cx) - 1,这样就会使得cx变成FFFFH,判断此时cx中的内容不为0,则继续执行循环,因此就会出现死循环

在这里插入图片描述
解决方案:在每次开始内存循环时,先将外层循环的值保存起来

改进后的程序
assume cs:codesg,ds:datasg

datasg segment
	db 'ibm.............'
	db 'dec.............'
	db 'vax.............'	
	db 'dos.............'
datasg ends

codesg segment

start: mov ax,datasg    ;把数据段的地址给到AX寄存器
	
	mov ds,ax			;ds为数据位的寄存器,ds和偏移地址构成
	mov bx,0			;bx寄存器设置为0
	
	mov cx,4			;外层循环指的是层数的循环
s0:mov dx,CX			;将cx中的数据放入到dx中
	mov si,0			;si寄存器和bx寄存器的时候几乎一致
	
	mov cx,3			;内层循环为列的循环	
S:	mov al,[bx+si]      ;si控制列bx控制行
	and al,11011111b
	mov [bx+si],al
	inc si
	loop s
	
	add bx,16
	mov cx,dx
	loop s0
	
	mov ax,4c00H
	int 21H
	
codesg ends
end start

上面的程序用dx来暂时存放在cx中的值,如果在内层循环中,dx寄存器也被使用,我们就必须再寻找其他存储的位置------>内存

再次改进后的程序
assume cs:codesg,ds:datasg

datasg segment
	db 'ibm.............'
	db 'dec.............'
	db 'vax.............'	
	db 'dos.............'
	dw 0				;定义一个字用来存放cx
datasg ends

codesg segment

start: mov ax,datasg    ;把数据段的地址给到AX寄存器
	
	mov ds,ax			;ds为数据位的寄存器,ds和偏移地址构成
	mov bx,0			;bx寄存器设置为0
	
	mov cx,4			;外层循环指的是层数的循环
s0:mov ds:[40H],CX		;将cx中的数据放入到内存单元中
	mov si,0			;si寄存器和bx寄存器的时候几乎一致
	
	mov cx,3			;内层循环为列的循环	
S:	mov al,[bx+si]      ;si控制列bx控制行
	and al,11011111b
	mov [bx+si],al
	inc si
	loop s
	
	add bx,16
	mov cx,ds:[40H]
	loop s0
	
	mov ax,4c00H
	int 21H
	
codesg ends
end start

一般来说,在需要暂存数据的时候,我们都应该使用栈

继续修改--->使用栈段
assume cs:codesg,ds:datasg

datasg segment
	db 'ibm.............'
	db 'dec.............'
	db 'vax.............'	
	db 'dos.............'
datasg ends

stacksg segment              ;定义一个段,用来做栈段
	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
		   
		   mov cx,4
 	s0:    push cx
 		   mov si,0
 		   mov cx,3
 	s:     mov al,[bx+si]
 		   and al,11011111b
		   mov [bx+si],al
		   inc si
		   loop s
	
		   add bx,16
		   pop cx
		   loop s0
		   	
		   mov ax,4c00H
		   int 21H
	
codesg ends
end start

问题7.9:将代码段中的每个单词的前四个字母改为大写字母

在这里插入图片描述
思路:我们需要进行4x4次的二重循环,用变量R定位行,常量3定位每行要修改的起始列,变量C定位相对于起始列的要修改的列。外层循环按行来进行,内层按列来进行。我们首先用R定位第1行,循环修改R行的3+C(O≤C≤3)列;然后再用R定位到下一行,再次循环修改R行的3+C(O≤C≤3)列……,如此重复进行

assume cs:codesg,ds:datasg,ss:stacksg

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

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
		   mov si,0
		   mov cx,4
		   
	s:	   mov al,[bx+3+si]
		   and al,11011111b
		   mov [bx+3+si],al
		   inc si
		   loop s
		   
		   add bx,16
		   pop cx
		   loop s0
		   
		   mov ax,4c00H
		   int 21H
		   
codesg ends
end start  
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CYS.burst

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值