- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个初始化字符串“Hello, World!”我想从中提取第一个字符(即“H”)并将其转换为在运行时传递到寄存器的字符。
我尝试比较“Hello, World!”的第一个字符通过以下代码使用“H”:
global start
section .data
msg: db "Hello, World!", 10, 0
section .text
start:
mov rdx, msg
mov rdi, [rdx]
mov rsi, 'H'
cmp rdi, rsi
je equal
mov rax, 0x2000001
mov rdi, [rdx]
syscall
equal:
mov rax, 0x2000001
mov rdi, 58
syscall
但是,此代码终止时不会跳转到 equal
标签。而且,我的程序的退出状态是72
,这是H
的ASCII码。这让我尝试将 72
传递到 rsi
而不是 H
,但这也导致程序终止而没有跳转到 等于
标签。
如何正确比较“Hello, World!”中的第一个字符带有传递到寄存器的字符?
最佳答案
您和@Rafael 的答案使您的代码过于复杂。
您通常不想将 mov rdi, msg
与绝对地址的 64 位立即数一起使用。 (参见Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array)
使用default rel
并使用cmp byte [msg], 'H'
。或者,如果您希望 RDI 中的指针以便可以在循环中递增它,请使用 lea rdi, [rel msg]。
分支之间唯一不同的是 RDI 值。您不需要重复 RAX 设置或系统调用,只需在 RDI 中获取正确的值,然后让分支重新相互连接即可。 (或者无分支地进行。)
@Rafael 的答案出于某种原因仍然从字符串中加载 8 个字节,就像您问题中的两个加载一样。大概这是 sys_exit ,它忽略高字节,只从低字节设置进程退出状态,但只是为了好玩,让我们假设我们实际上希望为系统调用加载所有 8 个字节,而只比较低字节.
default rel ; use RIP-relative addressing modes by default for [label]
global start
section .rodata ;; read-only data usually belongs in .rodata
msg: db "Hello, World!", 10, 0
section .text
start:
mov rdi, [msg] ; 8 byte load from a RIP-relative address
mov ecx, 'H'
cmp dil, cl ; compare the low byte of RDI (dil) with the low byte of RCX (cl)
jne .notequal
;; fall through on equal
mov edi, 58
.notequal: ; .labels are local labels in NASM
; mov rdi, [rdx] ; still loaded from before; we didn't destroy it.
mov eax, 0x2000001
syscall
尽可能避免写入 AH/BH/CH/DH。它要么对 RAX/RBX/RCX/RDX 的旧值有错误的依赖,要么如果您稍后读取完整寄存器,可能会导致部分寄存器合并停止。 @Rafael 的答案并没有这样做,但是 mov ah, 'H'
取决于某些 CPU 上 AL 的负载。请参阅 Why doesn't GCC use partial registers? 和 How exactly do partial registers on Haswell/Skylake perform? Writing AL seems to have a false dependency on RAX, and AH is inconsistent - mov ah, 'H'
对 Haswell/Skylake 上 AH 的旧值有错误的依赖,即使 AH 与 RAX 分开重命名。但 AL 不是,所以是的,这很可能对负载有错误的依赖,阻止它并行运行并延迟 cmp
一个周期。
无论如何,这里的 TL:DR 是,如果不需要的话,你不应该乱写 AH/BH/CH/DH。读取它们通常没问题,但延迟可能会更糟。请注意,cmp dil, ah
不可编码,因为 DIL 只能通过 REX 前缀访问,而 AH 只能在没有 REX 前缀的情况下访问。
我选择了 RCX 而不是 RSI,因为 CL 不需要 REX 前缀,但由于我们需要查看 RDI (dil) 的低字节,所以我们在 cmp 上无论如何都需要 REX 前缀。我可以使用 mov cl, 'H' 来节省代码大小,因为对 RCX 旧值的错误依赖可能没有问题。
顺便说一句,cmp dil, 'H'
与 cmp dil, cl
一样有效。
或者,如果我们将零扩展的字节加载到完整的 RDI 中,我们可以使用 cmp edi, 'H'
而不是它的低 8 版本。 (零扩展加载是在现代 x86-64 上处理字节和 16 位整数的正常/推荐方法。合并到旧寄存器值的低字节通常会降低性能,这会导致性能下降。这就是 Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register? 的原因。)
我们可以用 CMOV 代替分支。对于代码大小和性能而言,这有时更好,有时则不然。
版本 2,仅实际加载 1 个字节:
start:
movzx edi, byte [msg] ; 1 byte load, zero extended to 4 (and implicitly to 8)
mov eax, 58 ; ASCII ':'
cmp edi, 'H'
cmove edi, eax ; edi = (edi == 'H') ? 58 : edi
; rdi = 58 or the first byte,
; unlike in the other version where it had 8 bytes of string data here
mov eax, 0x2000001
syscall
(这个版本看起来要短很多,但大多数额外的行都是空格、注释和标签。优化为 cmp
-immediate 使得这 4 条指令而不是5 在 mov eax
/syscall
之前,但除此之外它们是相等的。)
关于assembly - 如何将字符串的第一个字符与 x86-64 汇编中的另一个字符进行比较?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54342324/
我被告知“汇编”是您在文件中编写的内容,让您的“汇编程序”将其转换为二进制代码。 但我看到这两个术语在各种作品中混合搭配。我什至听说你编写了“汇编器”,然后“汇编器”使其可执行。 正确的用词是什么?
我在正确终止用 Assembly 编写的 16 位 DOS 程序时遇到问题。这是部分代码: .386P .model flat stack_s segment stack 'stack'
我需要多少档才能正确执行以下指令。我对我所做的事情有些困惑,所以我在这里看到专家的答案。 lw $1,0($2); beq $1,$2,Label; 请注意,检查是否会发生分支将在解码阶段完成。但是在
我正在尝试在汇编中进行简单的乘法运算,但是由于某些原因,当标记了MUL函数时,我看不到寄存器会发生变化。 mov bx, 5 mov cx, 10 mul cx 最佳答案 这些称为指令,它们指定
我正在尝试在 Assembly 中实现递归斐波那契程序。但是,我的程序崩溃了,出现了未处理的异常,我似乎无法找出问题所在。我不怀疑这涉及我对堆栈的不当使用,但我似乎无法指出哪里...... .386
我编写了以下代码: .386 .model small .stack 100h .data text db "Paper",0 .code start : lea dx ,
我有一个用汇编语言编写的裸机 ARM 的启动代码,我正在尝试了解它是如何工作的。该二进制文件被写入一些外部闪存中,并在启动时将其自身的一部分复制到 RAM 中。尽管我读过这篇文章wikipedia e
我在数据部分定义了一个二维数组和两个一维数组(一个用于列总和,一个用于行总和),并且我编写了一个函数,将二维数组求和到一维数组中。我使用 eax 和 ebx 作为二维数组的索引,但是当 eax 或 e
我正在开始组装,我正在使用 nasm 来组装代码,我正在尝试处理驻留在内存中的字符串并更改它,我想检查一个字节是否在某个范围内(ascii),这样我就可以决定如何处理它,我似乎不知道如何检查一个值是否
虽然您通常不希望将一个整体程序集用于小型项目以外的任何事情,但可能会将事物分离得太多。 组装分离过多的迹象/气味是什么? 最佳答案 第一个(明显的)是:在一个有很多项目的解决方案中,其中只有少数(比如
我正在尝试编写斐波那契的汇编代码版本,它给出第 n 个斐波那契数并返回它。 出于某种原因,它在存储斐波那契数的返回值和添加它们时遇到问题。 我希望它打印第 n 个斐波那契数。 我对我的代码做了一些修改
我有一个最小的、可重现的示例有两个问题,该示例具有三个针对 .NET Core 3.1 的项目。但我也想以 .NET Standard 2.0 为目标。 该示例适用于需要在运行时加载程序集并使用提供的
: 运算符在汇编中做什么?代码如下:DS:DX我还没有找到该运算符(operator)的任何文档。(我正在使用 NASM) 最佳答案 那实际上只是一个寄存器分隔符,而不是运算符。这意味着使用 DX 寄
我在哪里可以找到为 gmp-5.0.0 编写的程序的汇编代码我正在使用 UBUNTU 和 G++ 编译器..编译代码的命令是“g++ test.cc -o outp -lgmp” 实际上我想知道在 1
我是组装新手,我有一个关于如何表示负数的问题 我有三个 DWORDS 变量,比如说: result DWORD 0 i DWORD 3 j DWORD 5 我想计算这个公式:result = i -
我想编写我的第一个汇编程序。我在论文上做了一些程序,但这是我第一次使用编译器。我正在使用 ideone .我的程序很简单, 翻译 A = 5 - A到 assembly NEG A ADD A, 5
程序集,masm 嘿,我写了宏来打印存储在 dane1 段中的 1 字节值。 我将值除以 16,然后将提醒推送到堆栈,直到值==0。然后我弹出提醒将它们转换为 ASCII 码,并打印它们。 有人可以看
我正在研究 nasm 的一个大学项目。唯一的问题是我无法生成 162 和 278 之间的偶数随机数。我尝试了很多算法,但似乎无法限制范围内的数字。 是否有一个小技巧或调整来获得所需的范围内的数字?目的
终于在无数次错误的漫长 session 之后,希望这是最后一个。 没有编译或运行时错误,只是一个逻辑错误。 编辑:(固定伪代码) 我的伪代码: first = 1; second = 1; thir
我知道在程序集r0中调用函数时,包含第一个参数,直到r3是第四个。我知道,当它超过四个时,将使用堆栈指针,但是我不太确定具体细节。 r0-r3仍然保持前四个,其余的进入堆栈吗?我正在看下面的程序集,试
我是一名优秀的程序员,十分优秀!