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

文章目录

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

相关推荐
sweetying1 小时前
30了,人生按部就班
android·程序员
用户2018792831672 小时前
Binder驱动缓冲区的工作机制答疑
android
Asort2 小时前
JavaScript 从零开始(七):函数编程入门——从定义到可重用代码的完整指南
前端·javascript
真夜2 小时前
关于rngh手势与Slider组件手势与事件冲突解决问题记录
android·javascript·app
华仔啊2 小时前
主线程存了用户信息,子线程居然拿不到?ThreadLocal 背锅
java·后端
用户2018792831672 小时前
浅析Binder通信的三种调用方式
android
艾小码2 小时前
用了这么久React,你真的搞懂useEffect了吗?
前端·javascript·react.js
干就完了12 小时前
js对象常用方法都在这,使用时想不到?不存在的
前端·javascript
艾小码2 小时前
还在硬邦邦跳转页面?Vue这3招让应用丝滑如德芙!
前端·javascript·vue.js
间彧2 小时前
Spring Boot项目中,Redis 如何同时执行多条命令
java·redis