文章目录
- 前言
- 1. 指令要处理的数据有多长?
- 1.1 通过寄存器指明数据的尺寸
- 1.1.1 字操作
- 1.1.2 字节操作
- 1.2 用操作符X ptr指明内存单元的长度
- 1.2.1 访问字单元
- 1.2.2 访问字节单元
- 1.2.3 为什么要用操作符X ptr指明
- 1.3 其他方法
- 2. 寻址方式的综合应用
- 2.1 问题背景(公司基本信息)
- 2.2 提出问题(公司信息的变化)
- 2.3 问题的分析与求解
- 2.3.1 分析要修改的数据
- 2.3.2 确定修改方法
- 2.4 程序的实现
- 2.5 用C语言来描述这个程序
- 2.6 根据C语言风格修改汇编程序
- 2.7 总结
- 结语
前言
📌
汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。
本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。
1. 指令要处理的数据有多长?
8086CPU的指令,可以处理两种尺寸的数据,byte和word。所以在机器指令中要指明,指令进行的是字操作还是字节操作。
对于这个问题,汇编语言中用以下方法处理。
1.1 通过寄存器指明数据的尺寸
通过使用8位寄存器还是16位寄存器,来进行字操作还是字节操作。
1.1.1 字操作
下面的指令中,寄存器指明了指令进行的是字操作:
mov ax,1
mov bx,ds:[0]
mov ds,ax
mov ds:[0],ax
inc ax
add ax,1000
1.1.2 字节操作
下面的指令中,寄存器指明了指令进行的是字节操作:
mov al,1
mov al,bl
mov al,ds:[0]
mov ds:[0],al
inc al
add al,100
1.2 用操作符X ptr指明内存单元的长度
在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度,X在汇编指令中可以为word或byte。
1.2.1 访问字单元
下面的指令中,用word ptr指明了指令访问的内存单元是一个字单元。
mov word ptr ds:[0],1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx],2
1.2.2 访问字节单元
下面的指令中,用byte ptr指明了指令访问的内存单元是一个字节单元。
mov byte ptr ds:[0],1
inc byte ptr [bx]
inc byte ptr ds:[0]
add byte ptr [bx],2
1.2.3 为什么要用操作符X ptr指明
在没有寄存器参与的内存单元访问指令中,用word ptr或byte ptr显性地指明所要访问的内存单元的长度是很必要的。否则,CPU无法得知所要访问的单元是字单元,还是字节单元。
假设我们用Debug查看内存的结果如下:
2000:1000 FF FF FF FF FF FF……
那么指令:
mov ax,2000H
mov ds,ax
mov byte ptr [1000H],1
将使内存中的内容变为:2000:1000 01 FF FF FF FF FF……
而指令:
mov ax,2000H
mov ds,ax
mov word ptr [1000H],1
将使内存中的内容变为:2000:1000 01 00 FF FF FF FF……
为什么?
应该不用我说了吧~呵呵~不过按照步骤还是说一下,因为我们要兼顾New comer。
这是因为
mov byte ptr [1000H],1
访问的是地址为 ds:1000H的字节单元,修改的是 ds:1000H单元的内容;而
mov word ptr [1000H],1
访问的是地址为 ds:1000H 的字单元,修改的是 ds:1000H和ds:1001H两个单元的内容。
1.3 其他方法
有些指令默认了访问的是字单元还是字节单元,比如:push [1000H]
就不用指明访问的是字单元还是字节单元,因为push指令只进行字操作。
2. 寻址方式的综合应用
2.1 问题背景(公司基本信息)
下面我们通过一个问题来进一步讨论各种寻址方式的作用。
关于DEC公司的一条记录(1982年)如下:
公司名称:DEC
总裁姓名:Ken Olsen
排 名:137
收 入:40(40亿美元)
著名产品:PDP(小型机)
这些数据在内存中以下图所示的方式存放。
可以看到,这些数据被存放在seg段中从偏移地址60H起始的位置,
从seg:60起始以ASCII字符的形式存储了3个字节的公司名称;
从seg:60+3起始以ASCII字符的形式存储了9个字节的总裁姓名;
从seg:60+0C起始存放了一个字型数据,总裁在富翁榜上的排名;
从seg:60+0E 起始存放了一个字型数据,公司的收入;
从seg:60+10起始以ASCII字符的形式存储了3个字节的产品名称。
2.2 提出问题(公司信息的变化)
以上是该公司1982年的情况,到了1988年DEC公司的信息有了如下变化。
-
(1)Ken Olsen在富翁榜上的排名已升至38位
-
(2)DEC的收入增加了70亿美元
-
(3)该公司的著名产品已变为VAX系列计算机
我们提出的任务是,编程修改内存中的过时数据。
2.3 问题的分析与求解
2.3.1 分析要修改的数据
首先,我们应该分析一下要修改的数据:
-
(1)(DEC公司记录)的(排名字段)
-
(2)(DEC公司记录)的(收入字段)
-
(3)(DEC公司记录)的(产品字段)的(第一个字符)、(第二个字符)、(第三个字符)
2.3.2 确定修改方法
从要修改的内容,我们就可以逐步地确定修改的方法:
-
(1)我们要访问的数据是DEC公司的记录,所以,首先要确定DEC公司记录的位置:
R=seg:60
确定了公司记录的位置后,我们下面就进一步确定要访问的内容在记录中的位置。
-
(2)确定排名字段在记录中的位置:0CH。
- 修改R+0CH处的数据。
-
(3)确定收入字段在记录中的位置:0EH。
- 修改R+0EH处的数据。
-
(4)确定产品字段在记录中的位置:10H。
要修改的产品字段是一个字符串(或一个数组),需要访问字符串中的每一个字符。所以我们要进一步确定每一个字符在字符串中的位置。
- 确定第一个字符在产品字段中的位置:P=0。
- 修改R+10H+P处的数据:P=P+1。
- 修改R+10H+P处的数据:P=P+1。
- 修改R+10H+P处的数据。
2.4 程序的实现
根据上面的分析,程序如下:
mov ax,seg
mov ds,ax
mov bx,60h ;确定记录地址:ds:bx
mov word ptr [bx+0ch],38 ;排名字段改为38
add word ptr [bx+0eh],70 ;收入字段增加70
mov si,0 ;用si来定位产品字符串中的字符
mov byte ptr [bx+10h+si],’V’
inc si
mov byte ptr [bx+10h+si],’A’
inc si
mov byte ptr [bx+10h+si],’X’
2.5 用C语言来描述这个程序
如果读者熟悉C语言的话,我们可以用C语言来描述这个程序,大致应该是这样的:
struct company{ /*定义一个公司记录的结构体*/
char cn[3]; /*公司名称*/
char hn[9]; /*总裁姓名*/
int pm; /*排 名*/
int sr; /*收 入*/
char cp[3]; /*著名产品*/
struct company dec={"DEC","Ken 0lsen",137,40,"PDP" };
/*定义一个公司记录的变量,内存中将存有一条公司的记录*/
int main()
{
int i;
dec.pm=38;
dec.sr=dec.sr+70;
i=0;
dec.cp[i]='V';
i++;
dec.cp[i]='A';
i++;
dec.cp[i]='X';
return 0;
}
2.6 根据C语言风格修改汇编程序
我们再按照C语言的风格,用汇编语言写一下这个程序,注意和C语言相关语句的比对:
mov ax seg
mov ds,ax
mov bx,60h ;记录首址送BX
mov word ptr [bx].0ch,38 ;排名字段改为38
;C:dec.pm=38;
add word ptr [bx].0eh,70 ;收入字段增加 70
;C:dec.sr=dec.sr+70;
;产品字段改为字符串'VAX'
mov si,0 ;C:i=0;
mov byte ptr [bx].10h[si],'V'inc si ;dec.cp[i]='V';
inc si ;i++;
mov byte ptr [bx].10h[si],'A' ;dec.cp[i]='A';
inc si ;i++;
mov byte ptr [bx].10h[si],'X' ;dec.cp[i]='X'
2.7 总结
我们可以看到,8086CPU提供的如[bx+si+idata]的寻址方式为结构化数据的处理提供了方便。使得我们可以在编程的时候,从结构化的角度去看待所要处理的数据。
从上面我们可以看到,一个结构化的数据包含了多个数据项,而数据项的类型又不相同,有的是字型数据,有的是字节型数据,有的是数组(字符串)。
一般来说,我们可以用[bx+idata+si]的方式来访问结构体中的数据。
用bx定位整个结构体,用idata定位结构体中的某一个数据项,用 si 定位数组项中的每个元素。
为此,汇编语言提供了更为贴切的书写方式。如:
[bx].idata
、[bx].idata[si]
。
在C语言程序中我们看到,
如:dec.cp[i]
,dec是一个变量名,指明了结构体变量的地址,cp 是一个名称,指明了数据项cp的地址,而i用来定位cp中的每一个字符。
汇编语言中的做法是:bx.10h[si]
对比一下,是不是很相似?
结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!