• [汇编语言]更灵活的定位内存地址的方法



    一、and和or指令

    and指令逻辑与指令,按位进行与运算

    例如指令:

    mov al,01100011B
    and al,00111011B
    
    • 1
    • 2

    执行后:al = 00100011B

    通过该指令可以将操作对象的相应位设置为0,其他位不变

    例如:

    将al的第6位设置为0的指令是:and al,10111111B
    
    • 1

    or指令逻辑或指令,可以进行或运算

    例如指令:

    mov al,01100011B
    or al,00111011B
    
    • 1
    • 2

    执行后:al = 01111011B

    可以通过该指令将操作对象的相应位设置为1,其他位保持不变


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

    在汇编语言中,我们用’…'的方式来指明数据是以字符的形式给出的,编译器将他们转化为相对应的ASCII码,如下面的程序:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    上面的源程序中db 'unIX'相对应是db 75H,6EH,49H,58H分别对应unIX的ASCII码

    mov al,'a'相对应是mov al,61H,对应a的ASCII码

    在这里插入图片描述
    可以用d命令查看data段,debug下是以16进制数码和ASCII码字符的形式显示出其中的内容


    三、大小写转化问题

    A 十六进制41H
    a 十六进制61H

    下面考虑这样一个问题,在codesg下填写代码,将datasg中的第一个字符串转化为大写,第二个字符串转化为小写

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db 'BaSiC'
    	db 'iNfOrMaTiOn'
    datasg ends
    
    codesg segment
    start:
    	
    
    codesg ends
    
    end start
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    大写字符’A’的ASCII码值+20H就可以得到’a’
    小写字符’a’的ASCII码值-20H就可以得到’A’

    因此我们先判断大小写,看是否需要进行转化,以BaSiC讨论,程序的流程是这样的:

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db 'BaSiC'
    	db 'iNfOrMaTiOn'
    datasg ends
    
    codesg segment
    start:
    	mov ax,datasg
    	mov ds,ax
    	mov bx,0
    	mov cx,5
    
    s:	mov al,[bx] ;每个字符送入al
    	;如果(al)> 61H,则为小写字母的ASCII码,则sub al,20H
    	mov [bx],al ;转化完的字符再送回去
    	inc bx
    	loop s
    	
    	;...
    	
    codesg ends
    
    end start
    
    • 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

    目前问题是我们还没有学习判断的指令,所以我们如何用已学的指令来解决这个问题?

    从ASCII的二进制形式来看,除了第5位外(从0开始计算位数),大小写字母的其他各位都是一样的,大写字母ASCII的第五位为0,小写字母的第5位为1,这样子我们不管他们原先是大写还是小写,将它的第五位置为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
    	mov cx,5
    	
    s:	mov al,[bx] ;ds:bx送入al
    	and al,11011111B ;第五位设置为大写
    	mov [bx],al ;转变后的送入原单元
    	inc 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
    
    • 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

    四、[bx+idata]

    前面,我们用[bx]的方式来指明一个内存单元,还可以用一种更灵活的方式来指明内存单元:[bx+idata]表示一个内存单元,它的偏移地址为:(bx) = idata 也就是bx中的数值再加上idata

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

    也可以写为:mov ax,[200+bx]mov ax,200[bx]mov ax,bx.200

    这样子我们就可以用更高级的结构来看待要处理的数据:

    在codesg中填写代码,将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写

    现在我们可以使用[bx+idata]的方式来简化上面的程序,datasg中有两个字符串,一个起始地址为0,另外一个起始地址为5,我们可以将这两个字符串看作是两个数组,一个从地址0开始存放,一个是从5开始存放,我们就可以使用[0+bx],[5+bx]的方式在同一循环中定位这两个字符串中的字符。

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db 'BaSiC'
    	db 'iNfOrMaTiOn'
    datasg ends
    
    codesg segment
    start:
    	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,001000000b
    	mov [b+bx],al
    	inc bx
    	loop s
    	
    codesg ends
    
    end start
    
    • 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

    五、SI和DI

    SI和DI是8086中和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+123]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    用SI和DI实现将字符串’welcome to masm!'复制到他后面的数据区中

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db 'welcome to masm!'
    	db '................'
    datasg ends
    
    codesg segment
    start:
    	;...是从16字节开始存放的
    	mov ax,datasg
    	mov ds,ax
    	
    	mov si,0
    	mov di,16 ;ds:di指向复制的空间
    	
    	mov cx,8
    	
    s:	mov ax,[si] ; ds:si指向的内容送入ax
    	mov [di],ax ; ax送入ds:di
    	add si,2
    	add di,2
    	
    	loop s
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 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

    用一个16位寄存器进行传送,一次复制2个字节,一共循环8次

    我们还可以进行一些优化,使得程序更加简单:

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db 'welcome to masm!'
    	db '................'
    datasg ends
    
    codesg segment
    start:
    	;...是从16字节开始存放的
    	mov ax,datasg
    	mov ds,ax
    	
    	mov si,0
    	
    	mov cx,8
    	
    s:	mov ax,[si+0] ; ds:si指向的内容送入ax
    	mov [si+16],ax ; ax送入ds:di
    	add si,2
    	
    	loop s
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 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

    六、[bx+si]和[bx+di] 与 [bx+si+idata]和[bx+di+idata]

    现在我们还可以用更灵活的方式来指明一个内存单元:[bx+si][bx+di]

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

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

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

    该指令也可以写为如下形式:
    mov ax,[bx+200+si]
    mov ax,200[bx][si]
    mov ax,[bx][si].200
    mov ax,[bx].200[si]


    七、不同寻址方式的灵活引用

    前面用到了几种定位内存地址的方式,可以称为寻址方式

    1. [idata]用一个常量来表示地址,可以用于直接定位一个内存单元
    2. [bx]用一个变量来表示内存地址,用于间接定位一个内存单元
    3. [bx+idata]用一个变量和常量来表示地址,可在一个起始地址的基础上用变量间接定位内存单元
    4. [bx+si]用两个变量来表示地址
    5. [bx+si+idata]用两个变量和一个常量来表示地址

    编程,将datasg段中的每个单词的头一个字母改为大写字母

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db '1.file	'
    	db '2.edit	'
    	db '3.search'
    	db '4.view  '
    	db '5.options'
    	db '6.help	'
    datasg ends
    
    codesg segment
    	start:
    	
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们可以看到,在datasg中定义了6个字符串,每个长度为16个字节(有空格)他们是连续存放的,可以把他们看为是6行16列的二维数组,按照要求,需要修改每一个单词的第一个字母,即二维数组的每一行的第3列,相当于首行偏移地址为2

    我们只需要进行6次循环,用一个R变量定义行,用常量2定位列

    	R = 第一行地址
    	mov cx,6
    s:	改变R行,3列的字母为大写
    	R = 下一行的地址
    	loop s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    assume cs:codesg,ds:datasg
    
    datasg segment
    	db '1.file	'
    	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]
    	and al,11011111b
    	mov [bx+3],al
    	add bx,16
    	loop s
    	
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 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段中每个单词改为大写字母

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db 'fie				'
    	db 'edt				'
    	db 'sch				'
    	db 'vie				'
    
    datasg ends
    
    codesg segment
    	start:
    	
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    data中定义了4个字符串,每个长度为16个字节(为了使在Debug中方便查看,所以我们每个字符串后面加了空格符)可以把这4个字符串看成一个4行16列的二维数组

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db 'fie				'
    	db 'edt				'
    	db 'sch				'
    	db 'vie				'
    
    datasg ends
    
    codesg segment
    	start:
    	
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    assume cs:codesg,ds:datasg
    
    datasg segment
    	db '1.file	'
    	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 ;ds:ax
    	
    	mov cx,4	
    	
    s0:	mov dx,cx ;将外层循环cx保存在dx中
    	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
    	mov cx,dx
    	loop s0
    	
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 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

    上面的程序我们用dx来暂时存放cx的值,但是如果在内层循环中,dx寄存器也被使用,怎么办?因为CPU寄存器数量是有限的,那怎么办?

    这样子我们就不能考虑使用寄存器来存放,我们就使用内存来存放,这样子我们就需要去开辟一段内存空间

    assume cs:codesg,ds:datasg
    
    datasg segment
    	db '1.file	'
    	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 ;ds:ax
    	
    	mov cx,4	
    	
    s0:	mov ds:[40H],cx ;将外层循环的cx值保存在datasg:40单元中
    	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
    	mov cx,ds:[40H] ;用datasg:40H单元中的值来恢复cx
    	loop s0
    	
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 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

    上面的程序中,我们使用了内存单元来保存数据,但是上面的作法有些麻烦,因为如果需要保存多份数据的时候,我们需要记住数据存放在哪一个单元中,这样子程序容易混乱。一般来说,我们需要暂存数据的时候,我们都应该使用栈,所以可以再改进我们的程序

    assume cs:codesg,ds:datasg,ss:stacksg
    
    datasg segment
    	db 'fie		'
    	db 'edt		'
    	db 'sch		'
    	db 'vie 	'
    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		;将外层循环cx压入栈
    	mov si,0
    	mov cx,3	;cx为内层循环
    	
    s:	mov al,[bx+si]
    	and al,11011111b
    	mov [bx+si],al
    	
    	inc si
    	loop s
    	
    	and bx,16
    	pop cx
    	loop s0
    	
    	
    	mov ax,4c00h
    	int 21h
    	
    codesg ends
    
    end start
    
    • 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
  • 相关阅读:
    LeetCode 面试题 04.03. 特定深度节点链表
    AI ChatGPT 各大开放平台一览 大模型 Prompt
    Java高级——方法调用
    【Datahub】一站式元数据管理平台:Datahub宝典
    excel中公式结合实际的数据提取出公式计算的分支
    计算机毕业设计之java+ssm 精准扶贫系统
    BUUCTF [BJDCTF2020]just_a_rar 1
    华为ICT——第六章:深度学习和卷积神经网络/详篇
    SpringSecurity原理解析(五):HttpSecurity 类处理流程
    学网络安全可以参考什么方向?该怎么学?
  • 原文地址:https://blog.csdn.net/weixin_51304981/article/details/126718403