zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

2023-02-19 12:28:02 时间

目录

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 01000010B

I 49H 01001001B
i 69H 01101001B

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

assume cs:codesg,ds:datasg
datasg segment
	db 'XiaoChenYi '
	db 'I LOVE YOU'
datasg ends

codesg segment
start:
// 遇到小写字母就变大写
	mov ax,datasg
	mov ds,ax
	mov bx,0
	mov cx,10
 s: mov al,[bx]
	and al,11011111b
	mov [bx],al
	inc bx
	loop s
// 遇到大写变小写
	mov bx,11
	mov cx,10
s0: mov al,[bx]
	or al,00100000b
	mov [bx],al
	inc bx
	loop s0

	mov ax,4c00h
	int 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 segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends

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

assume cs:codesg,ds:datasg
datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
datasg ends 

codesg segment 
start:
  mov ax,datasg
  mov ds,ax
  mov bx,0
  mov cx,4
 s0: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
  loop s0

  mov ax,4c00h
  int 21h
codesg ends
end start

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

修改后的代码

assume cs:codesg,ds:datasg,ss:stacksg

datasg segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
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

   add bx,16
   pop cx      ; 外层循环cx值出栈
   loop s0      ; 外层循环

   mov ax,4c00h
   int 21h
codesg ends
end start

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

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