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

文章目录

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

assembly 复制代码
mov ax,2000H
mov ds,ax
mov bx,1000H
mov ax,[bx]
mov cx,[bx+1]
add cx,[bx+2]

思考后看分析。

1.4 问题一的分析与求解

assembly 复制代码
mov ax,[bx]   

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

assembly 复制代码
mov cx,[bx+1]    

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

assembly 复制代码
add cx,[bx+2]

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

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

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

2.1 问题引入

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

assembly 复制代码
assume cs:codesg,ds:datasg

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

codesg segment
 start: ......
codesg ends

end start

2.2 原来的解决方案

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

assembly 复制代码
	   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 改进后的程序

改进后的程序如下:

assembly 复制代码
	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 还可以写成这样

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

assembly 复制代码
 	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语言来描述上面的程序,大致是这样的:

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!'复制到它后面的数据区中。

assembly 复制代码
 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 指向复制的目的空间,然后用一个循环来完成复制。

代码段如下:

assembly 复制代码
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]的方式,来使程序变得简洁。

程序如下:

assembly 复制代码
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中的内容是什么:

assembly 复制代码
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 问题四的分析与求解

assembly 复制代码
mov ax,[bx+si]    

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

assembly 复制代码
mov cx,[bx+si]    

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

assembly 复制代码
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中的内容是什么。

assembly 复制代码
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 问题五的分析与求解

assembly 复制代码
mov ax,[bx+2+si]   

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

assembly 复制代码
mov ax,[bx+2+si] 

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

assembly 复制代码
mov ax,[bx+2+si] 

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

结语

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

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

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

相关推荐
空の鱼几秒前
java开发,IDEA转战VSCODE配置(mac)
java·vscode
水瓶丫头站住25 分钟前
安卓APP如何适配不同的手机分辨率
android·智能手机
Coovally AI模型快速验证28 分钟前
MMYOLO:打破单一模式限制,多模态目标检测的革命性突破!
人工智能·算法·yolo·目标检测·机器学习·计算机视觉·目标跟踪
P7进阶路1 小时前
Tomcat异常日志中文乱码怎么解决
java·tomcat·firefox
xvch1 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
幽兰的天空1 小时前
介绍 HTTP 请求如何实现跨域
网络·网络协议·http
lisenustc1 小时前
HTTP post请求工具类
网络·网络协议·http
心平气和️1 小时前
HTTP 配置与应用(不同网段)
网络·网络协议·计算机网络·http
小丁爱养花2 小时前
Spring MVC:HTTP 请求的参数传递2.0
java·后端·spring
CodeClimb2 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od