(七)汇编语言——更灵活的定位内存地址的方法

news/2025/1/20 7:17:17/

目录

and和or

ASCII码

[bx+idata]

SI和DI寄存器

[bx+si]和[bx+di]

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

总结

例子(双重循环的解决方案)


        我们知道,对于汇编来说,内存是极为重要的,所以,能精准且巧妙地定位内存地址是非常重要的。接下来,我们就来简单介绍一下定位内存地址的几种方法吧!但是在这之前,我们还要学习一些其他的知识点,就让我们开始今天的学习吧!

and和or

        这个比较简单,就是按位与(and)和按位或(or),具体用法如下所示:

// 这个就是将第6位置为0,其他不变
and al,11011111b
// 这个就是将第6位置为1,其他不变
or al,00100000b

ASCII码

        这个比较简单,相信大家都清楚这个,但是我需要补充一点点,就是关于大小写字母的联系,大家可以看到,大小写之间就是第6位不同,其他都是相同的,这也为我们进行大小写转化提供了思路,只需要用与或命令即可。

// 大家可以看到,大小写之间就是第6位不同,其他都是相同的
b 62H 01100010B
B 42H 01000010BI 49H 01001001B
i 69H 01101001B

        接下来,我们学以致用,编写一个将大写字母转换成小写字母, 小写字母转换成大写字母的汇编程序,要求是:第一个字符串:小写字母转换为大写字母;第二个字符串:大写字母转换为小写字母,我们来看看源码。

assume cs:codesg,ds:datasg
datasg segmentdb 'XiaoChenYi 'db 'I LOVE YOU'
datasg endscodesg segment
start:
// 遇到小写字母就变大写mov ax,datasgmov ds,axmov bx,0mov cx,10s: mov al,[bx]and al,11011111bmov [bx],alinc bxloop s
// 遇到大写变小写mov bx,11mov cx,10
s0: mov al,[bx]or al,00100000bmov [bx],alinc bxloop s0mov ax,4c00hint 21h
codesg ends
end start

         我们可以看到,代码的运行是正确的,当然了,数据中包含了空格,不是字母,但是运行也是没有问题的哈!

[bx+idata]

        接下来我们就来讲一些难度稍微大一点的东西了,开始介绍各种寻址方式了,这部分较为硬核,而且比较多,接下来我们就开始介绍噢! 

        [bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata(bx中的数值加上idata,idata是常数,之前介绍过,就不再介绍了)。我们举个例子来详细介绍一下:

mov ax,[bx+200]

        就这一个指令,就代表着内存单元的段地址在ds中,偏移地址为200加上bx中的数值,数学化的描述为:(ax)=((ds)*16+200+(bx))。他还有有一些其他的写法,我们举个例子看看:

mov ax,200[bx]

        比如这个例子,看起来有点像C语言里面的数组,好的,那我们就这样去理解它,其中200,代表(ds)*16+200,是一个固定的地址,相当于数组的起始地址,然后[bx],代表着偏移地址,这么看起来,这个和C语言的数组还是很像的,准确的来说,C语言就是按照这个来设计的,哈哈哈,毕竟汇编是比C还要古老的语言。所以一些能用数组解的题目,我们都能用这种寻址方式去解了。

SI和DI寄存器

        SI、DI这两个寄存器我们管他叫变址寄存器,主要的功能和bx类似,但是SI和DI不能够分成两个8位寄存器来使用,这就是他们之间的区别。具体的用法我们就不在这里详细讲解,看我们后面的例子就能够理解其主要的用法。

  1. mov bx,0
  2. mov ax,[bx]
  3. mov si,0
  4. mov ax,[si]
  5. mov di,0
  6. mov ax,[di]

        我们只需要知道,这三种写法的效果是相同的就行,因为这三个寄存器的作用是相似的。

[bx+si]和[bx+di]

        [bx+si]表示一个内存单元的偏移地址为(bx)+(si)(即bx中的数值加上si中的数值)。数学表达式为(ax)=((ds)*16+(bx)+(si))。他有一个其他的写法:

mov ax,[bx][si]

        这样看起来是不是很像二维数组呢,猜对了。这个给人的感觉就像是二维数组,bx表示一个偏移地址,si表示另外一个偏移地址,这就像是我们的二维数组,哈哈哈。

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

        接下来我们组合一下上面的两种寻址方式,于是我们得到了上面这两种寻址方式:[bx+si+idata]和[bx+di+idata],表示一个内存单元偏移地址为(bx)+(si)+idata,即bx中的数值加上si中的数值再加上idata,数学化的描述:(ax)=((ds)*16+(bx)+(si)+idata)。他还有一个其他的写法:

mov ax,[bx].idata[si]

        这个大家看一下,是不是感觉很像C语言里面的结构体,没错,这就类似于结构体,这个的话就是用bx定位整个结构体,用idata定位结构体中的某一个数据项,用si定位数据项中的元素。我们可以看到,下面几个是等价的。

// c语言
person.name[i] = 'Y';// 汇编
mov byte ptr [bx].idata[si],'Y'

        这里面出现了 byte ptr ,接下来我们就来看一下这个是什么意思,其实这个主要是用来告诉CPU,我们需要处理的数据有多长,在没有寄存器参与的内存单元访问指令中,用word ptr(字)byte ptr(字节)显性地指明所要访问的内存单元的长度是很必要的,否则,CPU无法得知所要访问的单元是字单元,还是字节单元。

总结

形式名称特点特点示例
[idata]直接寻址用一个常量/立即数来表示地址用于直接定位一个内存单元mov ax,[200]
[bx]寄存器间接寻址用一个变量来表示内存地址用于间接定位一个内存单元

mov bx,0

mov ax,[bx]

[bx+idata]寄存器相对寻址用一个变量和常量表示地址可在一个起始地址的基础上用变量间接定位一个内存单元

mov bx,4

mov ax,[bx+200]

[bx+si]基址变址寻址用两个变量表示地址mov ax,[bx+si]
[bx+si+idata]相对基址变址寻址用两个变量和一个常量表示地址mov ax,[bx+si+200]

例子(双重循环的解决方案)

        首先,我们来看一下一个例子:编程将datasg段中每个单词改为大写字母。

datasg segmentdb 'ibm             'db 'dec             'db 'dos             'db 'vax             '
datasg ends

        因为有4个字符串,我们可以把它看成一个4行16列的二维数组,我们要修改二维数组的每一行的前3列,所以我们构造一个4x3次的二重循环去解决。

assume cs:codesg,ds:datasg
datasg segmentdb 'ibm             'db 'dec             'db 'dos             'db 'vax             '
datasg ends codesg segment 
start:mov ax,datasgmov ds,axmov bx,0mov cx,4s0:mov si,0mov cx,3s:mov al,[bx+si]and al,11011111bmov [bx+si],alinc siloop sadd bx,16loop s0mov ax,4c00hint 21h
codesg ends
end start

        这个代码看起来没什么问题,但是,两次循环都共用了一个寄存器CX,导致循环错误,是得不到正确结果的,那么应该怎么修改呢,我们最后的方法是采用栈去解决。

修改后的代码

assume cs:codesg,ds:datasg,ss:stacksgdatasg segmentdb 'ibm             'db 'dec             'db 'dos             'db 'vax             '
datasg ends stacksg segmentdw 0,0,0,0,0,0,0,0    ;定义的栈
stacksg endscodesg segment
start:mov ax,stacksgmov ss,axmov sp,16mov ax,datasgmov ds,axmov bx,0mov cx,4s0:push cx      ; 外层循环cx值压栈mov si,0mov cx,3      ; cx设置为内层循环的次数s:mov al,[bx+si]and al,11011111bmov [bx+si],alinc siloop sadd bx,16pop cx      ; 外层循环cx值出栈loop s0      ; 外层循环mov ax,4c00hint 21h
codesg ends
end start

        这样就可以完美解决双重循环的问题了!

        好啦,关于寻址,我们就先讲解这么多,继续加油哦!还有一点,就是千万不能使用中文空格!!!千万不能使用中文空格!!!千万不能使用中文空格!!!


http://www.ppmy.cn/news/9904.html

相关文章

node.js快速入门指南

Node.js迅速蹿红,衍生了一个强大的开源社区、支持企业,甚至还拥有属于自己的技术大会。我把这种成功归结于它的简介,高校,同时提高了编程生产力。 Node.js 的前置知识很多,例如以下知识 JavaScriptES6Ajax 还不会的…

Compose跨平台第一弹:体验Compose for Desktop

前言 Compose是Android官方提供的声明式UI开发框架,而Compose Multiplatform是由JetBrains 维护的,对于Android开发来说,个人认为学习Jetpack Compose是必须的,因为它会成为Android主流的开发模式,而compose-jb作为一…

寒假本科创新——机器学习(二)

绪论1.3归纳偏好 一般原则:奥卡姆剃刀 什么样的算法比较好?1.4NFL定理 NFL定理的前提: NFL定理的寓意:1.3归纳偏好 归纳偏好(lnductive Bias): 机器学习算法在学习过程中对某种类型假设的偏好…

JAVA并发编程工具篇--1.1理解Future获取线程执行结果

背景:在并发编程中,我们可以使用Future来获取子线程执行的结果,然后在主线程一起进行业务处理; 那么Future是如何来工作的; 1 使用: demo1:使用Future每次都阻塞获取任务的执行结果&#xff1a…

android 换肤框架搭建及使用 (3 完结篇)

本系列计划3篇: Android 换肤之资源(Resources)加载(一)setContentView() / LayoutInflater源码分析(二)换肤框架搭建(三) — 本篇 tips: 本篇只说实现思路,以及使用,具体细节请下载代码查看! 本篇实现效果: fragment换肤recyclerView换肤自定义view属性换肤打开打开打开动…

Java Bean Validation

JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。校验框架注解如下: 注解解释Null被注释的元素必须为nullNotNull被注释…

成为有钱人的终极秘诀:做到这7步,你也可以成为富人!

经常有人问:互联网有什么快速赚钱的方法?大多数人内心浮躁,总想以最快的方式搞到钱。因为浮躁,所以沉不下心来去搞钱。做一个项目赚不到钱,然后又开始找项目,换项目,做项目,一直恶性循环中。最…

whistle的使用【前端抓包】

前言 抓包工具看起来只是测试要用的东西,其实对前端作用也很多,因为我们也要模拟请求、mock数据、调试。站在巨人肩膀上永远不亏! whistle能解决的痛点 一、看请求不方便 跳页、支付时候上一页的请求结果看不到,h5、小程序newWork不能在电…