汇编语言裁剪字符串代码分析(11)?

 

 

 

来自于《Intel汇编语言程序设计》(第四版)第八章。

 

 

裁剪字符串过程从以空字符串结尾的字符串中删除特定字符之后的所有字符。

 

需要的知识:

 

1.

重复前缀。

字符串操作指令本身每次只处理一个内存值。但如果增加一个重复前缀的话,该指令就会使用ECX作为计数器进行重复。

几个重复前缀如下:

 

REP                      当ECX>0时重复

REPZ,REPE           当零标志被设置并且ECX>0时重复

REPNZ,REPNE       当零标志被清除并且ECX>0时重复

 

2.

SCASB,SCASW和SCASD指令,将AL/AX/EAX中的值同目标内存中的字节,字或双字相比较,目标内存数据是由EDI寻址的

这些指令在一个长字符串或数组中查找一个值的时候特别有用。

如果使用REPE(或REP)前缀,当ECX>0并且AL/AX/EAX不能匹配内存中的值时,指令将继续扫描字符串或数组。

REPNE前缀使得指令扫描字符串直到AL/AX/EAX匹配内存中的一个值ECX=0为止。

 

例如下面扫描匹配字符的例子,在字符串变量alpha中查找字符"F",如果找到了该字母,EDI指向匹配字符串后面的一个字符(如果没有找到匹配项,就会执行JNZ指令退出):

 

.data

alpha BYTE "ABCDEFGH",0

.code

mov edi,OFFSET alpha              ; EDI points to the string

mov al,'F'                                  ; search for the letter F

mov ecx,LENGTHOF alpha         ; set the search count

cld                                             ; direction = up

repne scasb                              ; repeat while not equal 

jnz quit                                     ; quit if letter not found

dec edi                                      ; found : back up EDI

 

整个程序的重点就在于红色部分,他会一直循环,直到匹配了F或者ECX=0为止。甚至在循环完之后,还会对是这两种条件的哪一种进行判断。如果是ECX=0(就是循环了所有的字符之后仍未找到),则直接quit;如果不是,则必定是因为匹配了F,则将EDI后退一个字节,指向被匹配的这个字符。

 

 

在Str_trim中,我们也要用到scasb

 

 

3.

字符串指令使用CPU的方向标志来决定ESI和EDI是自动增加还是自动减少:

 

可以使用CLD和STD指令显式地改变,CLD将清除方向标志,STD设置方向标志,其造成影响如下所示:

 

方向标志被清除:ESI和EDI自动增加,寻址顺序按照从低到高。

方向标志被设置:ESI和EDI自动减少,寻址顺序按照从高到低。

 

 

 

 

 

 

下面我们来看一下裁剪字符串程序源代码:

 

;----------------------------------------------------------------------------

Str_trim PROC USES eax ecx edi,

   pString : PTR BYTE,                           ; points to string

   char : BYTE                                        ; char to remove

;

; remove all occurrences of a given character from

; the end of a string.

; Returns : nothing

;----------------------------------------------------------------------------

         mov edi,pString

         INVOKE Str_length,edi        ; returns length in EAX

         cmp eax,0                           ; zero-length string ?

         je L2                                    ; yes : exit

         mov ecx,eax                        ; no : counter = string length

         dec eax

         add edi,eax                         ; EDI points to last char

         mov al , char                        ; char to trim

         std                                       ; direction = reverse

         repe scasb                           ; skip past trim character

         jne L1                                   ; remove first character ?

         dec edi                                 ; adjust EDI : ZF=1 && ECX=0

L1:    mov BYTE PTR [edi+2],0       ; insert null byte

L2:    ret

Str_trim ENDP

        

        

程序首先使用:

 

USES eax ecx edi,

 

将这几个寄存器的值压进堆栈保存起来。

 

然后函数接受两个参数:

 

   pString : PTR BYTE,                           ; points to string

   char : BYTE                                        ; char to remove

 

 

 第一个是指向字符串的地址,第二个是待查找删除的字符。

 

下面是程序体:

 

 

         mov edi,pString                  ; 程序首先将接收到得pString地址保存到edi中

         INVOKE Str_length,edi        ; 调用Str_length过程求的接收到的pString的长度,长度值会被保存到eax中

         cmp eax,0                           ; 长度值是否为0?

         je L2                                    ; 如果为零,则跳转到L2,程序结束。

         mov ecx,eax                        ; 如果不为零,则将字符串长度保存到ecx中

         dec eax                               ; eax中的长度值减1

         add edi,eax                         ; 将edi指向字符串pString的最后一个字符

         mov al , char                        ; 将要查找删除的字符保存到al中

         std                                       ; ESI和EDI将在执行字符串指令时自动减少,并且寻址顺序是由高到低

         repe scasb                           ; 没看懂

         jne L1                                   ; 没看懂

         dec edi                                 ; 没看懂

L1:    mov BYTE PTR [edi+2],0       ; 没看懂

L2:    ret

 

 

后面代码没看懂,始终不明白为什么要将edi指向最后一个字符,后面的那些操作也不清楚。

<think>我们有一个RT-Thread项目的构建脚本,需要逐行解析。脚本中涉及RT-Thread的构建系统,使用了SCons作为构建工具。我们将按照脚本的代码顺序进行解析,并解释每一行或相关代码块的作用。首先,我们注意到脚本中引用了`rtconfig`模块,该模块通常包含目标平台(如处理器架构、工具链等)的配置信息。同时,脚本还使用了RT-Thread构建系统中的`building`模块,该模块提供了一些构建函数(如`PrepareBuilding`、`DoBuilding`等)和工具。下面是逐行解析:```pythonimportosimportsysimportrtconfig```-导入必要的Python模块:-`os`:提供操作系统相关功能,如文件路径操作、环境变量等。-`sys`:提供与Python解释器交互的功能,如模块搜索路径`sys.path`。-`rtconfig`:导入当前项目的配置模块(通常是一个`rtconfig.py`文件),其中定义了目标平台、工具链、编译选项等。```pythonifos.getenv('RTT_ROOT'):RTT_ROOT=os.getenv('RTT_ROOT')else:RTT_ROOT=os.path.normpath(os.getcwd()+'/../../..')```-设置`RTT_ROOT`变量,表示RT-Thread操作系统的根目录。-首先尝试从环境变量`RTT_ROOT`中获取。-如果环境变量不存在,则假设当前目录是位于RT-Thread项目中的`bsp`(板级支持包)目录下,通过回溯三级目录(`'/../../..'`)来定位RT-Thread根目录,并使用`os.path.normpath`规范化路径。```pythonsys.path=sys.path+[os.path.join(RTT_ROOT),'tools']```-将RT-Thread根目录下的`tools`目录添加到`sys.path`中,以便后续导入`building`模块(因为`building.py`通常位于`tools`目录下)。```pythontry:frombuildingimport*except:print('CannotfoundRT-Threadrootdirectory,pleasecheckRTT_ROOT')print(RTT_ROOT)exit(-1)```-尝试从`building`模块导入所有函数和变量(`building`模块是RT-Thread构建系统的核心模块,提供构建函数)。-如果导入失败,打印错误信息并退出,提示用户检查`RTT_ROOT`。```pythonTARGET='rtthread.'+rtconfig.TARGET_EXT```-设置构建目标(可执行文件)的名称。`rtconfig.TARGET_EXT`是目标平台的文件扩展名(如`.elf`、`.axf`等),所以`TARGET`可能是`rtthread.elf`等。```pythonDefaultEnvironment(tools=[])env=Environment(tools=['mingw'],AS=rtconfig.AS,ASFLAGS=rtconfig.AFLAGS,CC=rtconfig.CC,CFLAGS=rtconfig.CFLAGS,AR=rtconfig.AR,ARFLAGS='-rc',CXX=rtconfig.CXX,CXXFLAGS=rtconfig.CXXFLAGS,LINK=rtconfig.LINK,LINKFLAGS=rtconfig.LFLAGS)```-`DefaultEnvironment(tools=[])`:初始化默认的构建环境(清空工具列表)。-创建一个新的SCons环境`env`,并指定工具为`'mingw'`(这里可能是指类Unix环境下的GCC工具链,尽管名为`mingw`,但实际工具由`rtconfig`指定)。-设置汇编器(`AS`)及其标志(`ASFLAGS`)。-设置C编译器(`CC`)及其标志(`CFLAGS`)。-设置归档工具(`AR`,用于创建静态库)及其标志(`ARFLAGS`,这里固定为`'-rc'`)。-设置C++编译器(`CXX`)及其标志(`CXXFLAGS`)。-设置链接器(`LINK`)及其标志(`LINKFLAGS`)。这些工具和标志都来自`rtconfig`模块。```pythonenv.PrependENVPath('PATH',rtconfig.EXEC_PATH)```-将`rtconfig.EXEC_PATH`(工具链可执行文件的路径)添加到环境变量`PATH`的最前面,确保构建时能够找到正确的工具链。```pythonifrtconfig.PLATFORMin['iccarm']:env.Replace(CCCOM=['$CC$CFLAGS$CPPFLAGS$_CPPDEFFLAGS$_CPPINCFLAGS-o$TARGET$SOURCES'])env.Replace(ARFLAGS=[''])env.Replace(linkcom=env["LINKCOM"]+'--maprtthread.map')```-如果目标平台是`iccarm`(IARforARM),则进行特定调整:-替换C编译命令(`CCCOM`)为指定的字符串格式。-替换归档标志(`ARFLAGS`)为空字符串(IAR的归档工具可能不需要额外标志)。-替换链接命令(`linkcom`),在原有链接命令(`LINKCOM`)基础上添加`--maprtthread.map`,生成链接映射文件。```pythonExport('RTT_ROOT')Export('rtconfig')```-使用SCons的`Export`函数,将`RTT_ROOT`和`rtconfig`这两个变量导出,使得在后续通过`SConscript`加载的子脚本中可以直接使用这些变量。```pythonSDK_ROOT=os.path.abspath('./')```-获取当前目录(即脚本所在目录)的绝对路径,赋值给`SDK_ROOT`(通常这是板级支持包BSP的目录)。```pythonifos.path.exists(SDK_ROOT+'/libraries'):libraries_path_prefix=SDK_ROOT+'/libraries'else:libraries_path_prefix=os.path.dirname(SDK_ROOT)+'/libraries'```-确定库文件的根目录(`libraries_path_prefix`):-如果当前目录下存在`libraries`目录,则使用`SDK_ROOT/libraries`。-否则,使用上一级目录下的`libraries`目录(`os.path.dirname(SDK_ROOT)`得到当前目录的父目录)。```pythonSDK_LIB=libraries_path_prefixExport('SDK_LIB')```-将库文件根目录赋值给`SDK_LIB`,并导出该变量,供子脚本使用。```pythonobjs=PrepareBuilding(env,RTT_ROOT,has_libcpu=False)```-调用`building`模块中的`PrepareBuilding`函数,准备构建环境。该函数会:-配置编译环境(如添加全局的编译标志)。-扫描BSP目录下的源代码,并返回一个由源文件编译后的目标文件(Object)列表。-参数说明:-`env`:当前构建环境。-`RTT_ROOT`:RT-Thread根目录。-`has_libcpu`:是否包含`libcpu`(处理器相关的移植代码)。这里设置为`False`,表示不包含(可能因为该BSP已经将处理器相关代码放在其他位置)。```pythonapm32_library='APM32F10x_Library'rtconfig.BSP_LIBRARY_TYPE=apm32_library```-设置一个字符串变量`apm32_library`为`'APM32F10x_Library'`,然后将其赋值给`rtconfig.BSP_LIBRARY_TYPE`(可能用于后续的库选择)。```pythonobjs.extend(SConscript(os.path.join(libraries_path_prefix,apm32_library,'SConscript')))```-使用`SConscript`函数加载指定的SCons构建脚本(位于库目录下的`APM32F10x_Library/SConscript`),该脚本会构建对应的库(例如,APM32F10x的外设库),并将其生成的目标文件列表添加到`objs`中。```pythonobjs.extend(SConscript(os.path.join(libraries_path_prefix,'Drivers','SConscript')))```-同样,加载并构建`Drivers`目录下的驱动代码(如板级外设驱动),并将目标文件添加到`objs`。```pythonDoBuilding(TARGET,objs)```-调用`building`模块中的`DoBuilding`函数,执行实际的构建过程。该函数会:-将之前收集的所有目标文件(`objs`)链接成最终的可执行文件(`TARGET`)。-处理构建过程中的依赖关系。###总结该脚本完成了以下任务:1.设置RT-Thread根目录(`RTT_ROOT`)。2.配置构建环境(工具链、编译选项等)。3.准备BSP的源代码并编译。4.添加芯片外设库和驱动程序。5.将所有目标文件链接为最终的可执行文件。通过SCons的脚本组织,RT-Thread的构建系统实现了模块化,使得不同BSP和芯片库可以灵活组合。###相关问题§§相关问题§§1.在RT-Thread构建系统中,`rtconfig.py`文件通常包含哪些配置项?2.`building`模块中的`PrepareBuilding`函数具体完成了哪些工作?3.如何为新的芯片移植RT-Thread,需要修改哪些部分?4.在SCons构建系统中,`Export`函数的作用是什么?它是如何传递变量的?</think>以下是对您提供的 RT-Thread Python 构建脚本的逐行解析,结合引用内容和 RT-Thread 构建系统的特性进行详细说明: ### 脚本逐行解析 ```python import os import sys import rtconfig ``` - **功能**:导入必要的 Python 模块 - `os`:提供操作系统接口,用于路径操作和环境变量访问 - `sys`:提供系统相关功能,如模块路径管理 - `rtconfig`:导入 RT-Thread 的**目标平台配置文件**(包含芯片架构、工具链等设置)[^1] ```python if os.getenv('RTT_ROOT'): RTT_ROOT = os.getenv('RTT_ROOT') else: RTT_ROOT = os.path.normpath(os.getcwd() + '/../../..') ``` - **功能**:确定 RT-Thread 根目录 - 优先从环境变量 `RTT_ROOT` 获取路径 - 若未设置,则通过相对路径计算(假设当前在 `bsp/芯片型号` 目录) - `os.path.normpath` 规范化路径格式(处理 `../` 等符号) ```python sys.path = sys.path + [os.path.join(RTT_ROOT),'tools'] ``` - **功能**:添加 RT-Thread 构建工具路径 - 将 `$RTT_ROOT/tools` 加入 Python 模块搜索路径 - 使后续能导入 `building` 模块(RT-Thread 核心构建工具)[^3] ```python try: from building import * except: print('Cannot found RT-Thread root directory,please check RTT_ROOT') print(RTT_ROOT) exit(-1) ``` - **功能**:导入 RT-Thread 构建系统核心模块 - `building` 模块提供 `PrepareBuilding`, `DoBuilding` 等关键函数 - 若导入失败说明路径配置错误,退出并提示检查 `RTT_ROOT`[^3] ```python TARGET = 'rtthread.' + rtconfig.TARGET_EXT ``` - **功能**:设置输出文件名 - `rtconfig.TARGET_EXT` 来自目标平台配置(如 `.elf`, `.bin`) - 最终生成文件名如 `rtthread.elf` ```python DefaultEnvironment(tools=[]) env = Environment(tools = ['mingw'], AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, AR = rtconfig.AR, ARFLAGS = '-rc', CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) ``` - **功能**:配置交叉编译工具链 - `DefaultEnvironment` 初始化 SCons 构建环境 - `Environment` 创建实际构建环境,关键参数: - `tools=['mingw']`:指定 MinGW 工具集(Windows 环境) - 汇编器(`AS`)、C编译器(`CC`)、归档器(`AR`)、C++编译器(`CXX`)、链接器(`LINK`)均从 `rtconfig` 获取 - 各工具的标志参数(`*FLAGS`)同样来自平台配置[^1] ```python env.PrependENVPath('PATH',rtconfig.EXEC_PATH) ``` - **功能**:设置工具链搜索路径 - 将 `rtconfig.EXEC_PATH`(工具链二进制目录)添加到系统 `PATH` 最前面 - 确保构建时能正确调用 `arm-none-eabi-gcc` 等交叉编译工具 ```python if rtconfig.PLATFORM in ['iccarm']: env.Replace(CCCOM = ['$CC $CFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -o $TARGET $SOURCES']) env.Replace(ARFLAGS = ['']) env.Replace(linkcom = env["LINKCOM"] + ' --map rtthread.map') ``` - **功能**:IAR 工具链特殊处理 - 当使用 IAR for ARM(`iccarm`)时,调整编译命令格式 - 修改链接命令添加 `--map rtthread.map` 生成内存映射文件 - 清除归档器标志(`ARFLAGS`)避免冲突 ```python Export('RTT_ROOT') Export('rtconfig') ``` - **功能**:导出关键变量 - 使用 SCons 的 `Export` 使 `RTT_ROOT` 和 `rtconfig` 在子 SConscript 中可见 - 为后续组件构建提供全局配置[^1] ```python SDK_ROOT = os.path.abspath('./') ``` - **功能**:获取当前 BSP 目录 - `os.path.abspath('./')` 获取当前脚本所在目录的绝对路径 - 通常是板级支持包(BSP)目录,如 `bsp/apm32f103` ```python if os.path.exists(SDK_ROOT + '/libraries'): libraries_path_prefix = SDK_ROOT + '/libraries' else: libraries_path_prefix = os.path.dirname(SDK_ROOT) + '/libraries' ``` - **功能**:确定外设库路径 - 检查是否存在 `libraries` 目录(存放芯片外设库) - 若不存在则使用上级目录的 `libraries`(共享库方案)[^2] ```python SDK_LIB = libraries_path_prefix Export('SDK_LIB') ``` - **功能**:导出外设库路径 - 将确定的库路径赋值给 `SDK_LIB` - 导出到环境供子脚本使用 ```python objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False) ``` - **功能**:准备基础构建环境 - 调用 `building.PrepareBuilding()` 初始化 RT-Thread 内核构建 - 参数: - `env`:当前构建环境 - `RTT_ROOT`:RT-Thread 根目录 - `has_libcpu=False`:表示不使用标准 `libcpu` 目录(使用厂商库替代)[^1] - 返回内核目标文件列表 `objs` ```python apm32_library = 'APM32F10x_Library' rtconfig.BSP_LIBRARY_TYPE = apm32_library ``` - **功能**:指定芯片外设库 - 设置库名称(此处为 APM32F10x 芯片库) - 更新 `rtconfig` 配置,供后续组件使用[^2] ```python objs.extend(SConscript(os.path.join(libraries_path_prefix, apm32_library, 'SConscript'))) ``` - **功能**:构建芯片外设库 - 通过 `SConscript()` 加载并执行芯片库的构建脚本 - 将生成的库目标文件加入 `objs` 列表 - 路径如 `libraries/APM32F10x_Library/SConscript`[^2] ```python objs.extend(SConscript(os.path.join(libraries_path_prefix, 'Drivers', 'SConscript'))) ``` - **功能**:构建板级驱动 - 加载 `Drivers` 目录的构建脚本(板级外设驱动) - 将驱动目标文件加入 `objs` 列表 - 路径如 `libraries/Drivers/SConscript` ```python DoBuilding(TARGET, objs) ``` - **功能**:执行最终构建 - 调用 `building.DoBuilding()` 完成整个系统的链接和输出 - 参数: - `TARGET`:输出文件名(如 `rtthread.elf`) - `objs`:包含内核、库和驱动的目标文件列表 - 生成最终可执行文件[^1] ### 构建流程总结 ```mermaid graph TD A[初始化环境] --> B[配置工具链] B --> C[准备内核构建] C --> D[添加芯片外设库] D --> E[添加板级驱动] E --> F[链接生成固件] ``` ### 关键机制说明 1. **模块化构建**:通过 `SConscript` 分层组织代码 - 内核 (`src/`, `libcpu/`) - 芯片库 (`libraries/APM32F10x_Library`) - 板级驱动 (`libraries/Drivers`)[^1][^2] 2. **配置系统**: - `rtconfig.py` 提供目标平台配置 - `Kconfig` 实现组件裁剪(虽然未在脚本直接出现,但影响构建)[^4] 3. **路径管理**: - `RTT_ROOT` 定位 RT-Thread 源码 - `SDK_ROOT` 定位当前 BSP - `SDK_LIB` 定位外设库 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值