汇编 第七章 更灵活地定位内存地址

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

本章主要讲解一些更灵活的定位内存地址的方法和相关的编程方法

7.1 and和or指令

​ 1.and指令:逻辑与指令,按位进行与运算
​ 1.如:mov al,01100011B
​ and al,00111011B//后面和al 与运算放入al
​ 执行后:
​ al=00100011B
​ 2.通过and指令可将操作对象的相应位设为0,其他位保持不变
​ 例如al的第6位设为0:and al,10111111B
​ 例如al的第7位设为0:and al,01111111B
​ 例如al的第0位设为0:and al,11111110B
​ 2.or指令,逻辑或运算,按位进行或运算
​ 1.如:mov al,01100011B
​ or al,00111011B
​ 执行后:
​ al=01111011B
​ 2.通过该指令可将操作对象的相应位设为1,其他位不变
​ or al,01000000B;将al的第6位设为1
​ or al,10000000B;将al的第7位设为1
​ or al,00000001B;将al的第0位设为1

7.2 关于ASCII码

​ 一种编码方案,在计算机系统中通常被采用,8位

一个文本编辑过程中,就包含着按照ASCII编码规则进行的编码和解码。在文本编辑过程中,我们按一下键盘的a键,就会在屏幕上看到“a”。这是怎样一个过程呢?我们按下键盘的a键,这个按键的信息被送入计算机,计算机用ASCII 码的规则对其进行编码,将其转化为61H存储在内存的指定空间中;文本编辑软件从内存中取出 61H,将其送到显卡上的显存中;工作在文本模式下的显卡,用ASCII码的规则解释显存中的内容,61H被当作字符“a”,显卡驱动显示器,将字符“a”的图像画在屏幕上。我们可以看到,显卡在处理文本信息的时候,是按照ASCII码的规则进行的。这也就是说,如果我们要想在显示器上看到“a”,就要给显卡提供“a”的ASCII 码,61H。如何提供?当然是写入显存中。

7.3 以字符形式给出的数据

​ 1.在汇编程序中,可以使用’xxx’的方式指明数据是以字符的形式给出的
​ 2.编译器会将它们转化为相应的ASCII码
​ 3.例如 db(define byte 定义字节型数据)
​ 1.db ‘unIX’ ;相当于:db 75H,6EH,49H,58H
​ ‘u’、‘n’、‘I’、'X’的ASCII码分别为75H,6EH,49H,58H
​ 2.mov al,‘a’ ;相当于:mov al,61H
​ 'a’的ASCII码为61H
​ 4.ASCII码中,大写字母和小写字母之间的规律
​ 小写字母=大写字母+32
​ 小写字母=大写字母+20H
​ 大写字母从41H开始排,小写字母从61H开始排

大写 二进制 小写 二进制
A 01000001 a 01100001
B 01000010 b 01100010
C 01000011 c 01100011
D 01000100 d 01100100

7.4 大小写转换的问题

​ 1.方案一:
​ 1.识别出是该字节是表示一个的大写英文字符,还是小写的
​ 用于条件判断的汇编程序,目前还没有学到
​ 2.根据+20H 或者 -20H进行大小写转换
​ 2.方案二:
​ 1.若全部转化为大写,则将第5位置0
and al,11011111B
​ 2.若全部转化为小写,则将第5位置1
or al,00100000B

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] ;将ASCII码从ds:bx所指向的单元中取出
		and al,11011111B ;将al中的ASCII码的第5位置为o,变为大写字母
		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+常数]

​ mov ax,[bx+200]的含义:
​ 1.将一个内存单元的内容送入ax,这个内存单元的长度为2字节,存放一个入一个字单元, 该字单元的偏移地址为bx中的数值加上200,段地址在ds中
​ 2.也可以写成
​ 1.mov ax,200[bx]
​ 2.mov ax,[bx].200

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

​ 在codesg中填写代码,将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写
​ 1.我们观察datasg段中的两个字符串,一个的起始地址为0,另一个的起始地址为5
​ 2.我们可以将这两个字符串看作两个数组,一个从0地址开始存放,另一个从5开始存放
​ 3.我们可以用[0+bx]和[5+bx]的方式在同一个循环中定位这两个字符串中的字符
​ 4.注意这个数组的定位方式,对比C语言
​ C语言的数组定位方式:a[i],b[i], a、b是地址常量
​ 汇编语言的数组定位方式:0[bx],5[bx]
​ 所以:[bx+常数]的方式为高级语言实现数组提供了便利的机制

assume cs:codesg,ds:datasg
datasg segment
	db 'BaSiC'
	db 'MinIX'
datasg ends
codesg segment
start:
	mov ax,datasg
	mov ds,ax
	mov bx,0
	mov cx,5	;做5次循环,前提是两个字符串长度相等,可以放在同个循环中
circ:
	mov al,[bx]
	and al,11011111b
	mov [bx],al
	mov al,[bx+5];等价于mov al,5[bx];等价于mov al,[bx].5
	or al,00100000b
	mov 5[bx],al
	inc bx
	loop circ
	mov ax,4c00h
	int 21h
codesg ends
end start
7.7 SI和DI

​ 已经学过的10个寄存器:AX、BX、CX、DX、DS、CS、SS、ES、IP、SP
​ 1. SI和DI是8086CPU中和bx功能相近的寄存器,bx不够用,所以引进了SI和DI
​ 2. SI和DI(16位)不能够分成两个8位寄存器来使用【和bx的区别】
​ 3. 下面三组指令实现了相同的功能
​ 1.mov bx,0
​ mov ax,[bx]
​ 2.mov si,0
​ mov ax,[si]
​ 3.mov di,0
​ mov ax,[di]
​ 4. 下面三组指令也实现了相同的功能
​ 1.mov bx,0
​ mov ax,[bx+123]
​ 2.mov si,0
​ mov ax,[si+123]
​ 3.mov di,0
​ mov ax,[di+123]
​ 5. 用寄存器SI和DI实现将字符串’welcome to masm!'复制到它后面的数据区中
​ 通常用ds:si指向要复制的源始字符串 source
​ 通常用ds:di指向要复制的目的空间 destination
注意si、di是16位寄存器,循环中自增时,应该+2

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 di,16
	mov cx,8
circ:
	mov ax,[si]
	mov [di],ax
	inc di
	inc di
	inc si
	inc si
	loop circ
	mov ax,4c00h
	int 21h
code ends
end start

用一个si简化代码

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
		mov ax,4c00h
		int 21h
codesg ends
end start
7.8 [bx+si]和[bx+di]
1.[bx+si]和[bx+di]的含义类似,我们以[bx+si]为例进行讲解
	[bx+si]表示一个内存单元,它的偏移地址为bx中的数值加上si中的数值
    它的偏移地址在ds中
2.[bx+si]也可以写成[bx][si]
7.9 [bx+si+常数]和[bx+di+常数]
1.以[bx+Si+常数]为例讲解
	[bx+si+常量]表示一个内存单元,偏移地址为bx的值+si的值+常数
2.指令mov ax,[bx+si+常数]也可以写成如下形式
    1.mov ax,200[bx+si]
    2.mov ax,200[bx][si]
    3.mov ax,[bx].200[si]
7.10 不同的寻址方式的灵活应用

​ 1.总结几种定位内存的方法
​ 1.ds:[idata] idata是一个常量 【直接寻址】
​ 用一个常量来表示地址,可用于直接定位一个内存单元
​ 2.[bx] 【寄存器间接寻址】
​ 用一个寄存器的值来表示内存地址,可以间接定位一个内存单元
​ 3.[bx+idata] 【相对寻址】
​ 用一节寄存器的值和常量表示内存地址,可在一个起始地址的基础上用变量间接定位一个内存单元
​ 4.[bx+si] 两个变量表示地址 【基址变址寻址】
​ 5.[bx+si+idata] 两个变量和一个常量表示地址 【相对基址变址寻址】

可以看到,从[idata]一直到[bx+si+idata],我们可以用更加灵活的方式来定位一个内存单元的地址。这使我们可以从更加结构化的角度来看待所要处理的数据。下面我们通过一个问题的系列来体会CPU提供多种寻址方式的用意,并学习一些相关的编程技巧。

2.编程,给定数据段data,将data段中每个单词的头一个字母改写成大写字母

assume cs:code,ds:data
data segment
	db '1. file         '
	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
circ:
	mov al,[bx+3]
	and al,11011111b ;转换大写使第5位为0 and运算
	mov [bx+3],al
	add bx,16
	loop circ
	mov ax,4c00h
	int 21h
code ends
end start

在这里插入图片描述

3.编程,给定数据段data,将data段中的每个单词改为大写字母
1.【loop指令cx-1之后,在判断是否为0】
2.双重循环用汇编怎么实现?
应该在每次开始内循环的时候,将外层循环的cx的值保存起来,
在执行外层循环的loop指令前,在恢复外层循环的cx数值。
可以用寄存器来临时保存,也可以用栈空间(内存)保存【没有多余的寄存器】 更好的方法是使用:栈
1.使用寄存器实现

assume cs:code,ds:datasg
datasg segment
	db 'ibm             '
	db 'dec             '
	db 'dos             '
	db 'vax             '
datasg ends
code segment
start:
		mov ax ,datasg
		mov ds,ax
		mov bx,0
		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中存放的外层循环的计数值恢复cx
		loop s0 ;外层循环的loop指令将cx中的计数值减1
        mov ax,4c00h
        int 21h
code ends
end start
  1. 优化因为寄存器数量有限,所以使用内存暂存cx

    显然,我们不能选择寄存器,那么可以使用的就是内存了。可以考虑将需要暂存的数据放到内存单元中,需要使用的时候,再从内存单元中恢复。这样我们就需要开辟一段内存空间。再次改进的程序如下。

assume cs:code,ds:datasg
datasg segment
	db 'ibm             '
	db 'dec             '
	db 'dos             '
	db 'vax             '
	dw 0 ;定义一个字,用来暂存cx
datasg ends
code segment
start:  mov ax, datasg
		mov ds , ax
		mov bx,0
		mov cx,4
s0 : 	mov ds: [ 40H],cx ;将外层循环的cx值保存在datasg : 40H单元中
		mov si,o
		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,ds: [40H] ;用datasg : 40H单元中的值恢复cx
        loop s0 ;外层循环的loop指令将cx中的计数值减1
        mov ax,4c00H
        int 21H
code ends
end start

3.使用栈实现【更好的方法】

assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
	db 'ibm             '
	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
        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的值,恢复cx
		loop s0 ;外层循环的loop 指令将cx中的计数值减1
        mov ax,4c00h
        int 21h
codesg ends
end start

4.修改每行每个单词前4个字母为大写

assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
	db '1. display      '
	db '2. brows        '
	db '3. replace      '
	db '4. modify       '
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
        mov cx,4
s0:		push cx ;将外层循环的cx值压栈
        mov si,0
        mov cx,4 ; cx设置为内层循环的次数
s :		mov al, [bx+si+3]
		and al,11011111b
		mov [bx+si+3],al
		inc si
        loop s
        add bx,16
        pop cx ;从栈顶弹出原cx的值,恢复cx
		loop s0 ;外层循环的loop 指令将cx中的计数值减1
        mov ax,4c00h
        int 21h
codesg ends
end start
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值