【汇编语言】7-灵活的(5大)寻址方式


公粽号「专注Linux」,专注Linux内核开发

本系列将讲解《汇编语言》一书,本节讲解第7章——更灵活的定位内存地址的方法。

前面,我们用[0]、[bx]的方法,在访问内存的指令中,定位内存单元的地址。本章我们主要通过具体的问题来讲解一些更灵活的定位内存地址的方法和相关的编程方法。


本节速览
1.and和or指令
2.处理ascii码的本质讲解
3.5大寻址方式
4.综合测试-双重循环处理

一、and和or指令

1.定义

指令使用解释
and al,11111111b将ax寄存器与二进制数11111111b相,结果为11111111
or al,00000000b将ax寄存器与二进制数00000000b相,结果为00000000

2.实战

前置阅读:😉【DOSBox】1-debug

验证and和or的作用程序代码

验证and和or的作用程序代码

||

V

果然and和or的指令效果验证成功


二、处理ascii码的本质讲解

1.按下键盘到显示字符的过程

世界上有很多编码方案,有一种方案叫做ASCII编码,是在计算机系统中通常被采用的。

简单地说,所谓编码方案,就是一套规则,它约定了用什么样的信息来表示现实的对象。比如说,在ASCII编码方案中,用61H表示“a”,62H表示“b”。一种规则需要人们遵守才有意义。

在文本编辑过程中,我们按一下键盘的a键,就会在屏幕上看到“a”这是怎样一个过程呢?我们安下键盘的 a键,这个按键的信息被送入计算机,计算机用 ASCII 码的规则对其进行编码,将其转化为61H存储在内存的指定空间中;文本编辑软件从内存中取出61H,将其送到显卡上的显存中:工作在文本模式下的显卡,用ASCII码的规则解释显存中的内容61H被当作字符“a”,显卡驱动显示器,将字符“a”的图像画在屏幕上。

(至于这里怎么画的,我们就不必了解了,知识屏蔽即可。)我们可以看到,显卡在处理文本信息的时候,是按照ASCI码的规则进行的。这也就是说,如果我们要想在显示器上看到“a”写入显存中。

  • 就要给显卡提供“a”的ASCII码,61H。

  • 如何提供?当然是写入显存中。

  • 如何写入?当然是从键盘驱动。

2.利用ascii编程

源码:


1.查看数据段

本程序没有执行data段,仅仅是将数据写入数据段中。我们使用-d ds+10h:0000查看内容:

果然,ds=075a,那么076a:0000处就是我们真实的数据,查看到了写入的字符。

这个实验充分证明了可以使用字符代替写入显存中,本质上字符也会翻译成对应的ASCII码,这是本质。所以应该说字符是ASCII码的包装。

为什么ds=075a,那么076a:0000处才是我们真实的数据?请移步此处查看: 【DOSBox】2-debug可执行文件-DOS加载可执行文件的过程

2.查看代码段

直接使用r命令执行代码,并使用t命令往下执行:

注意源码执行的代码只有code段,code和data段都是编译器加载到内存的,所以data段就算CPU不执行也会存数据在内存中,code段也有数据,但是更精确的说,这里的数据时供CPU执行的指令!CPU执行这里的执行达到控制计算机完成寄存器赋值的目的。

显然此处使用’a’'b’的本质就是61H,62H。编译器都自动转化了。

三、5种灵活的寻址方式

(1)[idata]用一个常量来表示地址,可用于直接定位一个内存单元;

(2)[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元;

(3)[bx+idata]用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元;

(4)[bx+si]用两个变量表示地址;

(5)[bx+si+idata]用两个变量和一个常量表示地址。

可以看到,从[idata]一直到[bx+si+idata],我们可以用更加灵活的方式来定位一个内存单元的地址。这使我们可以从更加结构化的角度来看待所要处理的数据。

注意,所谓寻址方式,无非就是常数、寄存器、寄存器与寄存器、寄存器与常数这些叠加。不用记专用什么间接寻址、基址寻址等晦涩的名字。计算机是实践学科,不是靠记忆的!

下面我们通过个问题的系列来体会CPU提供多种寻址方式的用意,并学习一些相关的编程技巧。

四、实战——双重循环

1.问题

编程,将 datasg 段中每个单词的头一个字母改为大写字母

assume  cs:codesg,ds:datasg

datasg segment
   db '1. file    ' ;注意这里每一串16个字符,也就是16个字节每行
   db '2. edit    '
   db '3. search  '
   db '4. view    '
   db '5. options '
   db '6. help    '
datasg ends

codesg segment
   start:
codesg ends

end start

2.问题分析

1.大小写转换

注意,因为同一字符的大小的ASCII码之间仅仅相差32,这也正是2的5次方,所以完全可以使用码点增加32或者减少32来实现大小写转换,那么如何快速实现增加32或者减少32呢?

  • 显然,小写字符的32位一定是1,因为它的码点大;小写字符的32位一定是0,因为它的码点小;所以我们完全可以使用与或指令来完成特定二进制位的置0置1。

对应的C代码:
#include <stdio.h>

int main(void)
{
    int char_a = 'a';
    printf("%c %c\n", char_a, char_a & 0b11011111); // 减少32
    int char_A = 'A';
    printf("%c %c\n", char_A, char_A | 0b00100000); // 多出32
    return 0;
}

结果:

显然,这完成了大小写的转化!

对应汇编

使用-a命令实现在对应内存中写汇编代码,然后调节CPU执行之。

这段汇编代码实现了字符a的大小写问题,并且将结果并列写入内存1000:0000中。

查看结果:

原始地址1000:0010处什么都没有

||

V

执行过后,实现了大小写转换并且写入到了内存中。

2.正式的源码

①使用单层循环

显然,我们每次处理一行16个字节,那么有一个最初的变量用于记录每一行的起始地址,另一个变量用做偏移地址,遍历该行,并且该变量每到一行,都要重新置0,意为重新遍历。

assume  cs:codesg,ds:datasg

datasg segment
   db '1. file         ' ;注意这里每一串16个字符,也就是16个字节每行
   db '2. edit         '
   db '3. search       '
   db '4. view         '
   db '5. options      '
   db '6. help         '
datasg ends

codesg segment
   start:
     ;注意从编译器给定的地址datasg这里开始进行转换
     mov ax,datasg;用作数据起始地址
     mov ds,ax;用ds承接数据起始地址
     mov cx,6;设置循环次数
     mov bx,0;用做偏移地址
  s: and byte ptr ds:[bx+2], 0dfh
     add bx,16;每次增加16个字符,换到下一行
     loop s
codesg ends
end start

程序执行前的数据存放,显然这是首字母小写的。

程序执行后,首字母全大写了。

②使用双层循环

上面的程序是使用单层循环的,固定了偏移量为3,但是我们可以将这个环节变成循环。从而有了两层循环的代码:

assume cs:codesg,ds:datasg
    datasg segment
    db '1. file         ' ;注意这里每一串16个字符,也就是16个字节每行
    db '2. edit         '
    db '3. search       '
    db '4. view         '
    db '5. options      '
    db '6. help         '
    datasg ends

    codesg segment
start:
        mov ax,datasg
        mov ds,ax
        mov cx,6;外层循环
        mov bx,0;定位行数
    s1:
        push cx;保存外层循环
        mov cx,4;每一层的偏移,这里可以改为1,就是首字母大写,也可以改为4,就是四个字母大写
        xor si,si
        s2:
            and byte ptr ds:[bx+si+3],0dfh;最佳实践,bx基地址,si偏移地址,3是固定偏移    
            inc si
            loop s2
        add bx,16
        pop cx
        loop s1
        
        mov ax,4c00h    ; 程序正常退出
        int 21h
    codesg ends
end start

执行结果:

完美实现了二层循环的逻辑

重点在于二层循环,两个注意点:

二层循环,两个注意点
1.每一层循环都是用的cx,只不过使用pushpop进行保存前一层的循环次数
2.基地址使用bx,偏移地址使用si,记住了,这是计算有效地址的规定,也是最佳实践

结尾

好了,本节我们讲述了【汇编语言】第6章包含多个段的程序的基础知识,让我们再来回顾回顾:

包含多个段的程序
1.and和or指令是指?
2.用and和or指令进行大小写转换的原理是?
3.讲解ASCII码的本质
3.5大寻址方式是?
4.在X86中,循环次数、基地址、偏移地址分别是用什么寄存器保存?(cx,bx,si)
5.双重循环的循环次数转接是怎么实现的?

如果您不能对着这个表格给出每一项的讲解,那么,再把上面的内容好好复习一遍吧!

答案见评论区,欢迎大家讨论~


汇编语言

😉【汇编语言】1—基础硬件知识

😉【汇编语言】2—寄存器基础知识

😉【汇编语言】3-寄存器与内存的交互

😉【汇编语言】4-第一个完整的汇编程序

😉【汇编语言】5-[BX]和loop指令

😉【汇编语言】6-包含多个段的程序

😉【汇编语言】7-灵活的5大寻址方式

😉【DOSBox】1-debug

😉【DOSBox】2-debug可执行文件

😉【DOSBox】3-完整开发流程

C语言

😉【C语言】C Token(C89 C99 C11)

😉【C语言】指针基础

😉【C语言】数组基础

😉【C语言】结构体对齐

😉【C语言】华为C语言进阶测试

😉【C语言】触发刷新到磁盘的方式总结

😉【C语言】C语言文件操作的mode详解

😉【C语言】C语言文件知识全讲解

😉【C语言】从extern到头文件包含的最优实践

😉【C语言】C语言的关键字与重载机制

😉【C语言】长字符串的2种处理方式

😉【C陷阱与缺陷】-1-词法陷阱

😉【C陷阱与缺陷】-2-语法陷阱

😉【C陷阱与缺陷】-3-语义陷阱


Linux

Linux101系列

专注讲解Linux中的常用命令,共计发布100+文章。

😉【Linux101-1】ls

😉【Linux101-1】ls -l命令输出结果全解析

😉【Linux101-2】cd

😉【Linux101-3】cat

😉【Linux101-4】tac

😉【Linux101-5】head

😉【Linux101-6】tail

😉【Linux101-7】pwd

😉【Linux101-8】touch

😉【Linux101-9】cal

😉【Linux101-10】bc

😉【Linux101-11】df

😉【Linux101-12】uname

😉【Linux101-13】mkdir

😉【Linux101-14】gzip

😉【Linux101-15】tar

😉【Linux101-16】lsof

😉【Linux101-17】du

😉【Linux101-18】stat


Linux102系列

本系列将精讲Linux0.11内核中的每一个文件,共计会发布100+文章。

😉【Linux102】1-Makefile

😉【Linux102】2-Makefile.header

😉【Linux102】3-system.map

😉【Linux102】4-bootsect.s

😉【Linux102】5-setup.s

😉【Linux102】6-head.s

😉【Linux102-D】/boot

😉【Linux102】7-main.c


Linux内核精讲系列

和Linux内核102系列不同,本系列将会从全局描绘Linux内核的各个模块,而非逐行源码分析,适合想对Linux系统有宏观了解的家人阅读。

😉【Linux】学习Linux前必备的知识点

😉【Linux】Linux内核对进程的内存抽象

😉【Linux】Linux概述1-linux对物理内存的使用

😉【Linux】软件从写下到运行的全部流程

😉【Linux】CPU的三寻址:实模式、保护模式、长模式

😉【Linux】实模式与保护模式的寻址, 这次讲明白了!

😉【Linux】linux0.11的源码目录架构

😉【Linux】Makefile机制及基础详解

😉【Linux】编译并运行Linux0.11

😉【Linux】“进进内网文”—Linux的内核结构全貌

😉【Linux】linux的中断机制

😉【Linux】linux进程描述


关于小希

😉嘿嘿嘿,我是小希,专注Linux内核领域,同时讲解汇编C语言等知识。

我的wx:C_Linux_Cloud,期待与您学习交流!

加wx请备注哦


小希的座右铭:别看简单,简单也是难。别看难,难也是简单。我的文章都是讲述简单的知识,如果你喜欢这种风格:

不妨关注、评论、转发,让更多朋友看到哦~~~🙈

下一期想看什么?在评论区留言吧!我们下期见!

内容概要:本文档围绕六自由度机械臂的ANN人工神经网络设计展开,涵盖正向与逆向运动学求解、正向动力学控制,并采用拉格朗日-欧拉法推导逆向动力学方程,所有内容均通过Matlab代码实现。同时结合RRT路径规划与B样条优化技术,提升机械臂运动轨迹的合理性与平滑性。文中还涉及多种先进算法与仿真技术的应用,如状态估计中的UKF、AUKF、EKF等滤波方法,以及PINN、INN、CNN-LSTM等神经网络模型在工程问题中的建模与求解,展示了Matlab在机器人控制、智能算法与系统仿真中的强能力。; 适合人群:具备一定Ma六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)tlab编程基础,从事机器人控制、自动化、智能制造、人工智能等相关领域的科研人员及研究生;熟悉运动学、动力学建模或对神经网络在控制系统中应用感兴趣的工程技术人员。; 使用场景及目标:①实现六自由度机械臂的精确运动学与动力学建模;②利用人工神经网络解决传统解析方法难以处理的非线性控制问题;③结合路径规划与轨迹优化提升机械臂作业效率;④掌握基于Matlab的状态估计、数据融合与智能算法仿真方法; 阅读建议:建议结合提供的Matlab代码进行实践操作,重点理解运动学建模与神经网络控制的设计流程,关注算法实现细节与仿真结果分析,同时参考文中提及的多种优化与估计方法拓展研究思路。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值