在程序集问题中将大写字母转换为小写字母

2023-12-02

我正在写信将预设字符串从大写转换为小写。我目前正在将地址处的内容移至 8 位寄存器,然后以一种非常草率的方式测试 ASCII 值以查看它是否为大写。有没有更干净的方法来解决这个问题?

现在我从 ASCII 值中减去 65 并与 25 进行比较。由于大写字母是 ASCII (dec) 65-90,因此任何大写字母都将得到 0-25。

    .DATA
string  DB   "ATest This String?.,/[}", '$'
strSize DD  23
.CODE
strToLower  PROC
        LEA     EAX, string
        PUSH    EAX
        CALL    toLower2    ; write toLower2
        POP EAX
        LEA EAX, string     ; return char* to C++
        RET
strToLower  ENDP

;---------------------------------------------
;Procedure: Convert to LowerCase
;Input: Address in EBX
;       unsigned in AL for each letter
;Output: EAX will contain new string
;---------------------------------------------

toLower2    PROC    ;65-90 is upper, 97-122 is lower (XOR 32?)
            LEA EBX, string
            MOVE ECX, strSize
            PUSH AL     ; PUSH AL before manipulating it
loop1:      MOV AL, [EBX]   ; Put char into AL to manipulate
            XOR BL, BL          ;?????????????
            MOV BL, AL          ;Set condition here???
            SUB BL, 65          ;?????????????
            CMP BL, 25          ;if(i > 64 && < 91) i += 32;
            JA  NoCap           ;
            ADD AL, 32          ;Adds 32 to ASCII value, making lower 
NoCap:      MOV [EBX], AL
            INC EBX
            LOOP loop1
            POP AL      ;Replace/POP AL
            LEA EAX, string
toLower2    ENDP
            END

SUB 然后无符号比较是仅使用一个条件分支检查输入是否在特定范围内的好方法,而不是单独的比较和分支>= 'A' and <= 'Z'.

编译器尽可能使用这个技巧。也可以看看Agner Fog 的优化装配指南,以及其他链接x86标记 wiki 以获取有关编写高效 asm 的更多内容。

您甚至可以使用它通过一个分支来检测字母字符(小写或大写):OR 与 0x20 将使任何大写字符变为小写,但不会使任何非字母字符成为字母字符。这样做,然后使用无符号比较技巧来检查是否在小写范围内。 (或者以 AND 开头~0x20清除该位,强制大写)。我用了这个技巧关于翻转字母字符大小写而保留其他字符的答案.

是的,正如您所注意到的,ASCII 的设计使每个字母的大写/小写之间的差异只是翻转一位。每个小写字符都设置了 0x20,而大写字符则将其清除。 AND/OR/XOR 通常更适合执行此操作(相对于 ADD/SUB),因为在强制采用一种情况时,有时您可以利用不关心初始状态的优势。


你的代码有一些奇怪的东西:PUSH AL甚至不能用大多数汇编器进行汇编,因为压入/弹出的最小大小是 16 位。保存/恢复 AL 也没有意义,因为在循环后恢复 AL 后,您会破坏整个 EAX!

此外,MOV 只是覆盖其目的地,因此无需xor bl,bl.

另外,您使用 BL 作为暂存寄存器,但它是 EBX 的低字节(您将其用作指针!)

以下是我的做法,仅使用 EAX、ECX 和 EDX,这样我就不必保存/恢复任何寄存器。 (您的函数会破坏 EBX,大多数 32 位和 64 位调用约定都需要函数来保存/恢复)。如果我需要一个额外的寄存器string不是静态分配的,让我可以使用它的地址作为直接常量。

toLower2    PROC    ;65-90 is upper, 97-122 is lower (XOR 32?)
            mov   edx, OFFSET string   ; don't need LEA for this, and mov is slightly more efficient
            add   edx, strSize         ; This should really be an equ definition, not a load from memory.

            ; edx starts at one-past-the-end, and we loop back to the start
loop1:
            dec   edx
            movzx eax, byte [edx]      ; mov al, [edx] leaving high garbage in EAX is ok, too, but this avoids a partial-register stall when doing the mov+sub in one instruction with LEA
            lea   ecx, [eax - 'A']     ; cl = al-'A', and we don't care about the rest of the register

            cmp    cl, 25              ;if(c >= 'A' && c <= 'Z') c |= 0x20;
            ja    NoCap
            or     al, 0x20            ; tolower
            mov   [edx], al            ; since we're branching anyway, make the store conditional
NoCap:
            cmp   edx, OFFSET string
            ja    loop1

            mov   eax, edx             
toLower2    ENDP

LOOP指令速度慢,应该避免。忘记它甚至存在并使用任何方便的循环条件。

仅在字符更改时才进行存储会使代码更加高效,因为如果无事可做,在一段时间没有更改的内存上使用它不会弄脏缓存。


代替ja NoCap,你可以用 cmov 无分支地做到这一点。但现在我必须忽略我的建议,更喜欢 AND/OR 而不是 ADD/SUB,因为我们可以使用 LEA 添加 0x20 而不影响标志,从而为我们节省一个寄存器。

loop1:
            dec   edx
            movzx eax, byte [edx]      ; mov al, [edx] leaving high garbage in EAX is ok, too, but this avoids a partial-register stall when doing the mov+sub in one instruction with LEA
            lea   ecx, [eax - 'A']     ; cl = al-'A', and we don't care about the rest of the register

            cmp    cl, 25              ;if(c >= 'A' && c <= 'Z') c += 0x20;
            lea   ecx, [eax + 0x20]    ; without affecting flags
            cmovna eax, ecx            ; take the +0x20 version if it was in the uppercase range to start with
            ; al = tolower(al)

            mov   [edx], al
            cmp   edx, OFFSET string
            ja    loop1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在程序集问题中将大写字母转换为小写字母 的相关文章

随机推荐

  • 通过 iTunes Connect 向 Apple 提交崩溃日志

    如何强制 iPhone 或 iPod Touch 向 Apple 发送 更新其崩溃日志 我的设备中的应用程序可能会崩溃 当我访问时iTunes连接 我没有看到任何崩溃日志 你不能强迫它 并且崩溃日志不会由设备上传到 Apple 当用户同步设
  • 可跨越协议

    我正在尝试转换以下 Swift 2 3 代码 Example usage 0 lt 778 binarySearch 0 lt 145 145 extension CollectionType where Index RandomAcces
  • 强制 SQL Remote 查询进行远程过滤而不是本地过滤

    我有一个 MS SQL 查询 它从远程服务器提取数据 我要提取的数据需要按运行时确定的日期进行过滤 当我像这样运行查询时 SELECT FROM SERVER Database dbo RemoteView WHERE EntryDate
  • Flutter:runZoned 函数上已弃用“onError”

    当我在 main dart 文件上使用最新的 Flutter v 1 17 1 和 Dart 2 8 2 版本时 收到此错误消息 onError 已被弃用 不应使用 请改用 runZonedGuarded 尝试用替换成员替换已弃用成员的使用
  • ,= 运算符

    在查看一些 Python 代码时 我注意到使用了类似的内容 操作员 a b 经过实验和非常仔细的检查赋值语句语法 我开始意识到这实际上是元组的 元组拆包 a 左侧长度为1的集合b在右边 因此 在一个简单的情况下 它类似于以下内容 但也适用于
  • vue-router — 未捕获(承诺中)错误:通过导航防护从“/login”重定向到“/”

    为什么 vue router 给我这个错误 需要明确的是 登录流程按预期工作 但我想 a 消除错误并 b 了解错误发生的原因 Error 未捕获 承诺中 错误 通过 a 重定向从 login 到 导航守卫 登录流程 开始注销 但输入需要身份
  • Xcode 6.类大小,在运行时看起来与 Interface Builder 和预览完全不同

    如同模拟器和故事板预览的区别 从左到右 故事板 故事板预览 模拟器 设备 我花了很多时间让这个工作在各种尺寸 任何 任何 紧凑 任何 常规 常规 等 所有在 预览 中看起来都很好 我thought应该模拟运行时 并且在模拟器 设备上看起来完
  • 如何在 ASP.NET Core 2 中添加自定义“无法反序列化”错误消息

    我正在构建一个 asp net core REST api 如何更改默认错误 无法将当前 JSON 数组 例如 1 2 3 反序列化为类型 Namespace Name Space Type 因为该类型需要 JSON 对象 例如 name
  • 在 Nexus 4 中从设备拉取 *.db 文件时出现“文件格式无效” - Android 5.1.1

    我正在学习Android开发 从设备打开拉取的数据库文件时 我总是收到此错误 文件格式无效 我用这个命令来拉 adb shell run as package chmod 666 data data package files databa
  • 参数化 Docker 镜像前缀

    我有一个 Dockerfile 其中包含以下内容 Build FROM ECR PREFIX maven 3 6 3 jdk 11 AS build COPY src home app src COPY pom xml home app R
  • 如何在 Java 中获取图形对象?

    我正在制作一个基本的太空入侵者游戏 我从 LWJGL zip 文件中获取了所有资源 我没有使用 LWJGL 库来创建我的游戏 只是从中获取了图片等 无论如何 每当我在键盘上按 空格 时 我的 KeyListener 都会创建一个新的我的船发
  • 获取 PostgreSQL 中视图/表所依赖的表列表

    在 PostgreSQL 中 有没有一种方法可以根据外键的使用和对给定表的访问来获取视图 表所依赖的所有表 基本上 我希望能够使用脚本复制视图 表的结构 并希望能够自动获取我还需要复制的表列表 以便一切正常工作 这个回应似乎正朝着正确的方向
  • 如何在 Crystal Report 中导出为 PDF 文件?

    使用 VB 6 和水晶报表 8 5 在客户端系统中生成报告时 我单击报告中的 导出为 PDF 按钮 它显示导出 导出选项 选择导出文件不显示在客户端系统中 但在我的系统中 显示的是 导出 导出选项 选择导出文件 但在客户端系统中不显示选择导
  • 为什么 eglMakeCurrent 因 EGL_BAD_ALLOC 失败?

    我正在使用 OpenGL ES 2 0 和 Android NDK r8b 我有一个用于工作线程的共享上下文 当我尝试使用eglMakeCurrent 将共享上下文绑定到工作线程时 收到错误EGL BAD ALLOC 现在让我困惑的是这段代
  • ASP.NET 应用程序可用的最大 RAM

    如果我购买一台可以放置任意 RAM 的服务器 那么使用最新版本的所有内容 我可以为我的 ASP NET Web 应用程序提供多少 RAM 我问的原因是我计划充分利用 InProc 会话的潜力 Thanks 32 位 Windows Serv
  • 如何使用 pip 安装 Pyomo 的“extras”?

    我写这篇文章是因为我在安装时遇到问题Pyomo using pipMac OSX 10 14 上的 Python 3 6 我正在按照安装说明进行操作here 我可以成功执行第一个命令 pip install pyomo 但是当我遇到麻烦时
  • Facebook sharer.php 是否已更改为不再接受详细参数?

    我们一直在打开一个共享弹出窗口 通过 window open 其 URL 如下 https www facebook com sharer sharer php s 100 p title EXAMPLE p summary EXAMPLE
  • Firebase 多位置规则 - 相同值

    这就是我最后想要的 data account K1472290187836 created 1472290190043 id K1472290187836 auth d182ddec f1c7 41c5 8b0e 198bfb5d9efe
  • D3 - 在垂直条形图中的矩形顶部获取文本

    我正在用 D3 成功制作垂直条形图 我还希望频率值位于每个矩形的顶部 我能够获得完整的结构和矩形 但是 我看不到我的价值观位于酒吧的顶部 有人可以帮忙吗 SNIPPET
  • 在程序集问题中将大写字母转换为小写字母

    我正在写信将预设字符串从大写转换为小写 我目前正在将地址处的内容移至 8 位寄存器 然后以一种非常草率的方式测试 ASCII 值以查看它是否为大写 有没有更干净的方法来解决这个问题 现在我从 ASCII 值中减去 65 并与 25 进行比较