【汇编语言】数据处理的两个基本问题(二) —— 解密汇编语言:数据长度与寻址方式的综合应用

文章目录

  • 前言
  • [1. 指令要处理的数据有多长?](#1. 指令要处理的数据有多长?)
    • [1.1 通过寄存器指明数据的尺寸](#1.1 通过寄存器指明数据的尺寸)
      • [1.1.1 字操作](#1.1.1 字操作)
      • [1.1.2 字节操作](#1.1.2 字节操作)
    • [1.2 用操作符X ptr指明内存单元的长度](#1.2 用操作符X ptr指明内存单元的长度)
      • [1.2.1 访问字单元](#1.2.1 访问字单元)
      • [1.2.2 访问字节单元](#1.2.2 访问字节单元)
      • [1.2.3 为什么要用操作符X ptr指明](#1.2.3 为什么要用操作符X ptr指明)
    • [1.3 其他方法](#1.3 其他方法)
  • [2. 寻址方式的综合应用](#2. 寻址方式的综合应用)
    • [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.4 程序的实现](#2.4 程序的实现)
    • [2.5 用C语言来描述这个程序](#2.5 用C语言来描述这个程序)
    • [2.6 根据C语言风格修改汇编程序](#2.6 根据C语言风格修改汇编程序)
    • [2.7 总结](#2.7 总结)
  • 结语

前言

📌

汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。

本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。

1. 指令要处理的数据有多长?

8086CPU的指令,可以处理两种尺寸的数据,byte和word。所以在机器指令中要指明,指令进行的是字操作还是字节操作。

对于这个问题,汇编语言中用以下方法处理。

1.1 通过寄存器指明数据的尺寸

通过使用8位寄存器还是16位寄存器,来进行字操作还是字节操作。

1.1.1 字操作

下面的指令中,寄存器指明了指令进行的是字操作

assembly 复制代码
mov ax,1
mov bx,ds:[0]
mov ds,ax
mov ds:[0],ax
inc ax
add ax,1000

1.1.2 字节操作

下面的指令中,寄存器指明了指令进行的是字节操作

assembly 复制代码
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指明了指令访问的内存单元是一个字单元

assembly 复制代码
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指明了指令访问的内存单元是一个字节单元

assembly 复制代码
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......

那么指令:

assembly 复制代码
mov ax,2000H
mov ds,ax  
mov byte ptr [1000H],1

将使内存中的内容变为:2000:1000 01 FF FF FF FF FF......

而指令:

assembly 复制代码
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 程序的实现

根据上面的分析,程序如下:

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

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语言相关语句的比对:

assembly 复制代码
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主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

相关推荐
沈剑心2 分钟前
Kotlin的协程,真能提升编程效率么?
android·前端·kotlin
froginwe119 分钟前
R 基础运算
开发语言
醉城夜风~9 分钟前
[数据结构]堆详解
开发语言·数据结构
堕落年代12 分钟前
Vue主流的状态保存框架对比
前端·javascript·vue.js
没资格抱怨14 分钟前
el-pagination的使用说明
javascript·vue.js·elementui
冴羽24 分钟前
Svelte 最新中文文档教程(22)—— Svelte 5 迁移指南
前端·javascript·svelte
用户611881615196228 分钟前
Java基础面试题
java
青红光硫化黑33 分钟前
React基础之useEffect
javascript·react.js·ecmascript
17´36 分钟前
Qt从入门到入土(八) -打包Qt程序
开发语言·c++·qt
AI+程序员在路上37 分钟前
QT显示网页控件QAxWidget、QWebEngineView及区别
开发语言·qt