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

devtools/2024/11/17 10:03:57/

在这里插入图片描述

文章目录

  • 前言
  • 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:datasgdatasg segmentdb 'BaSiC'db 'MinIX'
datasg endscodesg segmentstart: ……
codesg endsend start

2.2 原来的解决方案

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

	   mov ax,datasgmov ds,ax	mov bx,0	mov cx,5			s: mov al,[bx]		and al,11011111b		mov [bx],al	inc bx			loop smov bx,5mov cx,5		s0: mov al,[bx]or al,00100000b		mov [bx],alinc bxloop s0

2.3 新的解决方案

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

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

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

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

2.3.1 改进后的程序

改进后的程序如下:

	mov ax,datasgmov ds,axmov bx,0mov cx,5
s:	mov al,[bx]		;定位第一个字符串的字符and al,11011111bmov [bx],almov al,[5+bx]	;定位第二个字符串的字符or al,00100000bmov [5+bx],alinc bxloop s

2.3.2 还可以写成这样

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

 	mov ax,datasgmov ds,axmov bx,0mov cx,5
s:	mov al,0[bx]		;这里换了一种表达方式,下面也是。and al,11011111bmov 0[bx],almov al,5[bx]		or al,00100000bmov 5[bx],alinc bxloop 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:datasgdatasg 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,datasgmov ds,axmov si,0mov di,16mov cx,8s:   mov ax,[si]mov [di],axadd si,2add di,2loop smov ax,4c00hint 21h
codesg endsend start

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

3.4 问题三

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

思考后看分析。

3.5 问题三的分析与求解

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

程序如下:

codesg segment
start:   mov ax,datasgmov ds,axmov si,0mov cx,8s:   mov ax,0[si]mov 16[si],axadd si,2loop smov ax,4c00hint 21h
codesg endsend 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主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

在这里插入图片描述


http://www.ppmy.cn/devtools/134675.html

相关文章

[Linux]多线程详解

多线程 1.线程的概念和理解1.1线程的优点1.2线程的缺点1.3线程的设计1.4线程 VS 进程 2.线程控制2.1线程等待2.2 线程终止2.3 线程分离 3.线程互斥3.1背景3.2抢票代码演示3.3保护公共资源&#xff08;加锁&#xff09;3.3.1创建锁/销毁锁3.3.2申请锁/尝试申请锁/解锁 3.4解决抢…

MySQL社区版的启动与连接

1.启动&#xff1a; 注意&#xff1a;MySQL是默认开机自启的 方式一&#xff1a; 1.WinR 的命令行中直接输入services.msc 2.在服务中找到数据库名称&#xff0c;然后鼠标右键点击启动 方式二&#xff1a; 1.在开始选项中搜索“cmd”命令提示符&#xff0c;使用管理员身份运行 …

第八章利用CSS制作导航菜单

8.1 水平顶部导航栏 水平菜单导航栏是网站设计中应用最广泛的导航设计&#xff0c;一般放置在页面的顶部。 8.1.1 简单水平导航栏的设计与实现 8.1.1.1导航栏的创建 <nav>标签是HTML5新增的文档结构标签&#xff0c;用于标记导航栏&#xff0c;以便后续与网站的其他内…

linux文本处理三剑客grep,sed,awk的常用用法

1. grep命令 grep 命令在 Linux 系统中是文本搜索的利器&#xff0c;它基于正则表达式进行匹配&#xff0c;能够高效地筛选出包含指定模式的文本行。以下是 grep 命令的一些常用用法和示例&#xff1a; 基本用法 grep 模式 文件名 模式&#xff1a;可以是简单的文本字符串&…

3D Gaussian Splatting的全面理解

1.概述 高斯展开是一种表示 3D 场景和渲染新视图的方法,在“用于实时辐射场渲染的 3D 高斯展开” 中介绍。它可以被认为是类似 NeRF 的模型的替代品,就像过去的 NeRF 一样,高斯飞溅导致了许多新的研究工作,他们选择将其用作各种用例的 3D 世界的底层表示。那么它有什么特别…

主界面获取个人信息客户端方

主界面获取个人信息客户端方 前言 上一集我们完成了websocket身份验证的内容&#xff0c;那么这一集开始我们将要配合MockServer来完成主界面获取个人信息的内容。 需求分析 我们这边是完成客户端那方的内容&#xff0c;当客户端登录成功之后&#xff0c;我们就要从服务器获…

24 年第十届数维杯国际数模竞赛赛题浅析

本次万众瞩目的数维杯国际大学生数学建模赛题已正式出炉&#xff0c;无论是赛题难度还是认可度&#xff0c;该比赛都是数模届的独一档&#xff0c;含金量极高&#xff0c;可以用于综测加分、保研、简历添彩等各方面。考虑到大家解题实属不易&#xff0c;为了帮助大家取得好成绩…

Figma汉化:提升设计效率,降低沟通成本

在UI设计领域&#xff0c;Figma因其强大的功能而广受欢迎&#xff0c;但全英文界面对于国内设计师来说是一个不小的挑战。幸运的是&#xff0c;通过Figma汉化插件&#xff0c;我们可以克服语言障碍。以下是两种获取和安装Figma汉化插件的方法&#xff0c;旨在帮助国内的UI设计师…