【汇编语言】更灵活的定位内存地址的方法(二)—— 从 [bx+idata] 到 [bx+si+idata]:让你灵活的访问内存

时间:2024-11-17 07:06:44

在这里插入图片描述

文章目录

  • 前言
  • 1. [bx+idata]
    • 1.1 更加灵活的访问内存
    • 1.2 示例
    • 1.3 问题一
    • 1.4 问题一的分析与求解
  • 2. 用[bx+idata]的方式进行数组的处理
    • 2.1 问题引入
    • 2.2 原来的解决方案
    • 2.3 新的解决方案
      • 2.3.1 改进后的程序
      • 2.3.2 还可以写成这样
      • 2.3.3 用C语言来描述看看
    • 2.4 比较与总结
  • 3. SI和DI
    • 3.1 介绍两种寄存器
    • 3.2 问题二
    • 3.3 问题二的分析与求解
    • 3.4 问题三
    • 3.5 问题三的分析与求解
  • 4. [bx+si]和[bx+di]
    • 4.1 更加更加灵活的访问内存
    • 4.2 问题四
    • 4.3 问题四的分析与求解
  • 5. [bx+si+idata]和[bx+di+idata]
    • 5.1 更加更加更加灵活的访问内存
    • 5.2 问题五
    • 5.3 问题五的分析与求解
  • 结语

前言

????

汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。

本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为*处理器的PC机来进行学习。

1. [bx+idata]

1.1 更加灵活的访问内存

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

1.2 示例

我们看一下指令 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

1.3 问题一

Debug 查看内存,结果如下:

2000:1000 BE 00 06 00 00 00 …

写出下面的程序执行后,ax、bx、cx中的内容是什么。

mov ax,2000H
mov ds,ax
mov bx,1000H
mov ax,[bx]
mov cx,[bx+1]
add cx,[bx+2]

思考后看分析。

1.4 问题一的分析与求解

mov ax,[bx]   

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址在bx 中,(bx)=1000H;指令执行后(ax)=00BEH。

mov cx,[bx+1]    

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+1=1001H;指令执行后(cx)=0600H。

add cx,[bx+2]

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+2=1002H;指令执行后(cx)=0606H。

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

有了[bx+idata]这种表示内存单元的方式,我们就可以用更高级的结构来看待所要处理的数据。我们通过下面的问题来理解这一点。

2.1 问题引入

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

assume cs:codesg,ds:datasg

datasg segment
	db 'BaSiC'
	db 'MinIX'
datasg ends

codesg segment
 start: ……
codesg ends

end start

2.2 原来的解决方案

按照我们原来的方法,用[bx]的方式定位字符串中的字符。代码段中的程序如下:

	   mov ax,datasg
       mov ds,ax	
       mov bx,0	
       
       mov cx,5			
    s: mov al,[bx]		
       and al,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

2.3 新的解决方案

现在,我们有了 [bx+idata]的方式,就可以用更简化的方法来完成上面的程序。

观察datasg段中的两个字符串,一个的起始地址为0,另一个的起始地址为5。我们可以将这两个字符串看作两个数组,一个从0地址开始存放,另一个从5开始存放

那么我们可以用[0+bx]和[5+bx]的方式在同一个循环中定位这两个字符串中的字符。在这里,0和5给定了两个字符串的起始偏移地址,bx中给出了从起始偏移地址开始的相对地址

这两个字符串在内存中的起始地址是不一样的,但是,它们中的每一个字符,从起始地址开始的相对地址的变化是相同的。

2.3.1 改进后的程序

改进后的程序如下:

	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

2.3.2 还可以写成这样

程序也可以写成下面的样子:

 	mov ax,datasg
	mov ds,ax
	mov bx,0

	mov cx,5
s:	mov al,0[bx]		;这里换了一种表达方式,下面也是。
	and al,11011111b
	mov 0[bx],al
	mov al,5[bx]		
	or al,00100000b
	mov 5[bx],al
	inc bx
	loop s

2.3.3 用C语言来描述看看

如果我们用高级语言,比如C语言来描述上面的程序,大致是这样的:

char a[5]=“BaSiC”;
char b[5]=“MinIX”;
int main()
{
    int i;
    i=0;
    do
    {
        a[i]=a[i]&0xDF;
        b[i]=b[i]&0x20;
        i++;
    }while(i<5);
}

2.4 比较与总结

如果你熟悉C语言的话,可以比较一下这个C程序和上面的汇编程序的相似之处。尤其注意它们定位字符串中字符的方式

  • C语言定位方式:a[i],b[i]

  • 汇编语言定位方式:0[bx],5[bx]

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

3. SI和DI

3.1 介绍两种寄存器

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+123]

(2) mov si,0

​ mov ax,[si+123]

(3) mov di,0

​ mov ax,[di+123]

3.2 问题二

用寄存器si和di实现将字符串’welcome to masm!'复制到它后面的数据区中。

 assume cs:codesg,ds:datasg
    datasg segment 
      db 'welcome to masm!'
      db '................'
    datasg ends

思考后看分析。

3.3 问题二的分析与求解

我们编写的程序大都是进行数据的处理,而数据在内存中存放,所以我们在处理数据之前首先要搞清楚数据存储在什么地方,也就是说数据的内存地址。现在我们要对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

❗注意,在程序中,我们用16位寄存器进行内存单元之间的数据传送,一次复制 2 个字节,一共循环8次。

3.4 问题三

用更少的代码,实现问题二中的程序。

思考后看分析。

3.5 问题三的分析与求解

我们可以利用[bx(si或di)+idata]的方式,来使程序变得简洁。

程序如下:

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

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

4.1 更加更加灵活的访问内存

在前面,我们用[bx(si或di)]和[bx(si或di)+idata] 的方式来指明一个内存单元,我们还可以用更灵活的方式:

  • [bx+si]
  • [[bx+di]

(1)

[bx+si]和[bx+di]的含义相似,我们以[bx+si]为例进行讲解。

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

(2)

我们看下指令mov ax,[bx+si]的含义:

  • 将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为bx中的数值加上si中的数值,段地址在ds中。

  • 数学化的描述为:(ax)=( (ds)*16+(bx)+(si) )

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

4.2 问题四

用Debug查看内存,结果如下:

2000:1000 BE 00 06 00 00 00 ……

写出下面的程序执行后,ax、bx、cx中的内容是什么:

mov ax,2000H
mov ds,ax
mov bx,1000H
mov si,0
mov ax,[bx+si]
inc si
mov cx,[bx+si]
inc si
mov di,si
mov ax,[bx+di] 

思考后看分析。

4.3 问题四的分析与求解

mov ax,[bx+si]    

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)=1000H; 指令执行后(ax)=00BEH。

mov cx,[bx+si]    

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)=1001H; 指令执行后(cx)=0600H。

add cx,[bx+di]    

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(di)=1002H; 指令执行后(cx)=0606H。

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

5.1 更加更加更加灵活的访问内存

(1)

[bx+si+idata]和[bx+di+idata]的含义相似,我们以[bx+si+idata]为例进行讲解。

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

(2)

指令mov ax,[bx+si+idata]的含义:

  • 将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为bx中的数值加上si中的数值再加上idata,段地址在ds中。
  • 数学化的描述为:(ax)=( (ds)*16+(bx)+(si)+idata )
  • 该指令也可以写成如下格式(常用):
    • mov ax,[bx+200+si]
    • mov ax,[200+bx+si]
    • mov ax,200[bx][si]
    • mov ax,[bx].200[si]
    • mov ax,[bx][si].200

5.2 问题五

用Debug查看内存,结果如下:

2000:1000 BE 00 06 00 6A 22 ……

写出下面的程序执行后,ax、bx、cx中的内容是什么。

mov ax,2000H
mov ds,ax
mov bx,1000H
mov si,0
mov ax,[bx+2+si]
inc si
mov cx,[bx+2+si]
inc si
mov di,si
mov ax,[bx+2+di]

思考后看分析。

5.3 问题五的分析与求解

mov ax,[bx+2+si]   

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)+2=1002H; 指令执行后(ax)=0006H。

mov ax,[bx+2+si] 

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)+2=1003H; 指令执行后(cx)=006AH。

mov ax,[bx+2+si] 

访问的字单元的段地址在ds中,(ds)=2000H;偏移地址=(bx)+(si)+2=1004H; 指令执行后(cx)=226AH。

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

在这里插入图片描述