在汇编中为正则表达式 '[ab][^r]+r]' 实现匹配器
Implementing a matcher for the regex '[ab][^r]+r]' in assembly
我的汇编代码需要帮助。我需要使用编写代码,它会找到适合我的正则表达式的范围。
我的正则表达式:[ab][^r]+r
,所以首先我要查找是否有 'a' 或 'b',然后跳转到 'Start' 部分。现在我有一个问题如何只保存这封信的第一次出现。
程序应显示:5, 10
- 这意味着匹配字符串从第 5 个位置开始,长度为 10。我想保存在 'ecx'
和 'edx'
注册表中的程序结果(或者我可以简化它吗?)
我将不胜感激所有建议和帮助:)
这是一个代码:
#include <stdio.h>
int main(void)
{
char *s = "fqr b qabxx xryc pqr"; // string length= 22, first occurence: 5 position, second one: 9
int x, y;
asm volatile (
".intel_syntax noprefix;"
"mov eax, %2;"
"xor ecx, ecx;"
"xor edx, edx;"
"lea ebx, [eax];"
"loop:"
"mov al, [ebx];"
"or al, al;"
"jz Finish;"
"inc edx;"
"cmp al, 'a';"
"je Start;"
"cmp al, 'b';"
"je Start;"
"jmp JumpNext;"
"Start:"
"mov ecx, edx;"
"jmp loop;"
"JumpNext:"
"inc ebx;"
"jmp loop;"
"Finish:"
"mov %0, ecx;"
"mov %1, edx;"
".att_syntax prefix;"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);
printf("%d, %d \n", x, y);
return 0;
}
编辑:这是完成的代码:
#include <stdio.h>
int main(void)
{
char *s = "fqr b qabxx xryc pqr";
int x, y;
asm volatile (
".intel_syntax noprefix;"
"xor ecx, ecx;" // First occurrence of letter 'a' or 'b'
"mov edx, 1;" // Matching string length
"lea ebx, [%2];"
"loop:"
"mov al, [ebx];"
"cmp al, 0;"
"jz ThereisNoChars;"
"cmp al, 'a';"
"je FoundAorB;"
"cmp al, 'b';"
"je FoundAorB;"
"inc ecx;"
"inc ebx;"
"jmp loop;"
"FoundAorB:"
"inc ebx;"
"inc edx;"
"mov al, [ebx];"
"or al, al;"
"jz ThereisNoChars;"
"cmp al, 'r';"
"je isRafterAorB;"
"jne ThereIsNoR;"
"isRafterAorB:"
"mov edx, 1;"
"inc ebx;"
"inc ecx;"
"jmp loop;"
"ThereIsNoR:"
"inc ebx;"
"inc edx;"
"mov al,[ebx];"
"or al, al;"
"jz ThereisNoChars;"
"cmp al, 'r';"
"je Finish;"
"jmp ThereIsNoR;"
"ThereisNoChars:"
"mov ecx, 0;"
"mov edx, 0;"
"jmp Finish;"
"Finish:"
"mov %0, ecx;"
"mov %1, edx;"
".att_syntax prefix;"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);
printf("%d, %d \n", x, y);
return 0;
}
显示预期结果(5, 10)。这意味着,匹配的正则表达式从第 5 个位置开始,长度为 10
首先,你对自己的要求有点不清楚。当我第一次阅读您的 post 时,看起来您正试图用汇编程序 () 编写一个完整的 "regex" 例程。但是仔细观察,您似乎真正在做的只是 "hardcoding" 这个在汇编程序中非常具体的正则表达式搜索。这种误解可能是这个问题没有得到任何回应的原因。
其次,你应该和 谈谈,他显然和你在同一个 class。也许你们两个可以分享笔记。
第三,有人应该和你的导师谈谈他的作业。使用 gcc 的 "inline asm" 来教授 asm 可能是最难的方法。他讨厌他的学生吗?我对他提供的 "template" 印象不深,(显然?)您不允许更改。我可以在这里看到至少六处我要更改的东西。
第四,你说正则表达式字符串“[ab][^r]+r”应该为"fqr b qabxx xryc pqr"打印出5, 10
。我不确定那怎么可能。匹配从(从零开始)5 开始,但不在位置 10 结束:
1 2
0123456789012345678901
fqr b qabxx xryc pqr
^ ^
start end
结尾是位置 15。匹配字符串 (b qabxx xr
) 的长度为 11 个字符,因此显然您不是在寻找长度。第二个 'start' 出现在位置 8,第三个出现在位置 9,并且还有多个可能的端点。 None 其中解释了您应该在哪里获得“10”。我假设你的意思是 5, 15
.
综上所述,处理 [ab][^r]+r
基本上分为 3 个部分:
[ab]
查找 'a' 或 'b'。如果找不到字符串,则在遇到字符串结尾时出错并退出。
[^r]+
如果 (1) 后面的字母是 'r',转到 1.
r
遍历字符串的其余部分并在下一个成功退出 'r',或者在字符串末尾错误退出。
如果您不明白为什么会有这些部分,请尝试使用 https://regex101.com/r/E3nI1F/1(它可以让您尝试各种正则表达式搜索字符串以查看它找到的内容)。
查看您当前的代码,我认为您没有正确处理 (2) 或 (3)(实际上,我认为您根本没有处理它们)。虽然我会在您的代码中更改其他内容,但也许调整应该等到事情正常工作。
考虑到这是家庭作业,我不想只 post 编写我的代码。如果你只是 copy/paste 我的作品,你不会学到任何东西。
如果您想在添加 2 和 3 的作业后编辑您的问题,我可以再次审核或提供更多建议。如果你post一份工作副本,我可以分享我的,我们可以比较它们。
------------ 编辑 1 --------------
my teacher does not seem to hate us
哦?考虑这段代码(你的简化版本):
asm volatile (
"xor %0, %0;"
"mov %1, %2"
:"=r" (x), "=r" (y)
:"r" (s));
看起来很简单,对吧?将 x
归零,然后将 s
复制到 y
。但是,由于一些叫做 "early clobber" 的东西(参见 https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html 上的 '&'
),gcc 在优化时 可能 (不保证)会选择%0 和 %2(或者可能是 %1 和 %2)使用相同的寄存器。因此,当您将 %0 归零时,您也可以将 %2 归零。
这可以通过添加 & 符号来解决,以确保没有重叠:
:"=&r" (x), "=&r" (y)
但是你怎么会知道这个呢?知道这个细节并不能帮助你学习汇编程序。这只是关于 gcc 的内联 asm 工作原理的一个奇怪的怪癖。如果你正在编写一个实际的 asm 例程(这是我推荐的),你永远不需要知道这一点。
如果您使用符号名称,这不是更容易阅读吗?
asm volatile (
"xor %[x], %[x];"
"mov %[y], %[s]"
: [x] "=&r" (x), [y] "=&r" (y)
: [s] "r" (s));
我觉得它更容易阅读。但这是另一件与汇编语言无关的事情。这只是一个关于如何在使用 gcc 时将内联 asm 推入 c 代码的技巧(你应该 almost never 做的事情)。
还有什么?此模板的其他一些问题:volatile
限定符不属于此处。它缺少 "cc"
破坏。还有 "memory"
破坏。你最终破坏了比你需要的更多的寄存器。哦,为什么不直接告诉人们使用 -masm=intel
编译并避免使用“.intel_syntax noprefix;”和“.att_syntax 前缀;”垃圾(还有更多 gcc 怪癖)。
使用汇编语言可以有用。我并不是要说它不是。但是尝试使用 gcc 的内联 asm 充满了怪癖。由于可以从 C 代码调用用纯汇编程序编写的函数,并且由于该方法有 none 个这些问题,我只能得出结论,你被迫这样做是因为你对 him/her 和(s)他讨厌你。
------------ 编辑 2 --------------
既然你已经 post 编辑了工作代码(假设你已经修复了 "arb r"
),让我提供我的代码:
#include <stdio.h>
int main(int argc, char *argv[])
{
const char *s = "fqr b qabxx xryc pqr"; // Succeeds with 5,11
int x, y;
// Assumes s is not NULL.
// Return y=-1 on not found.
asm volatile (
".intel_syntax noprefix\n\t"
"lea ebx, [%2-1]\n\t" // ebx is pointer to next character.
"mov ecx, %2\n\t" // Save this since we aren't using earlyclobber...
"mov %1, -1\n" // ...so at this point, %2 might be dead.
// Note that ebx starts at s-1.
"Phase1:\n\t"
"inc ebx\n\t"
"mov al, [ebx]\n\t" // Read next byte.
"test al, al\n\t"
"jz Done\n\t" // End of string.
// Check for [ab]
"cmp al, 'a'\n\t"
"je Phase2\n\t"
"cmp al, 'b'\n\t"
"jne Phase1\n"
// Phase 2 - Found [ab], check for [^r]+
"Phase2:\n\t"
"mov al, byte ptr [ebx+1]\n\t"
"test al, al\n\t"
"jz Done\n\t" // End of string.
"cmp al, 'r'\n\t"
"je Phase1\n\t" // Found [^r]+, go look for another [ab]
"mov %0, ebx\n\t"
// Found [ab], and no [^r]+. Find r.
"mov ebx, 1\n"
"Phase3:\n\t"
"mov al, [%0 + ebx]\n\t" // Read next byte.
"inc ebx\n\t"
"test al, al\n\t"
"jz Done\n\t" // End of string.
"cmp al, 'r'\n\t"
"jne Phase3\n\t"
// Found r.
"sub %0, ecx\n\t" // Set (x)
"mov %1, ebx\n"
"Done:\n"
".att_syntax prefix"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);
printf("%d, %d \n", x, y);
return 0;
}
它更短,不需要那么多寄存器(没有 edx)。虽然它可以再调整一点,但它是解决家庭作业问题的可靠方法。
如果让你改一下框架,可能会好一点:
// Returns y = -1 if no regex match is found.
__asm__ (
// ---------------------------------
// Phase1 - look for [ab]
"mov %[x], %[s]\n" // Pointer to next char to read
"Phase1:\n\t"
"mov al, [%[x]]\n\t" // Read next byte
"test al, al\n\t"
"jz NotFound\n\t" // Hit end of string
"inc %[x]\n\t"
"cmp al, 'a'\n\t"
"je Phase2\n\t"
"cmp al, 'b'\n\t"
"jne Phase1\n"
// ---------------------------------
// Phase2 - Found [ab], Check for [^r]+
"Phase2:\n\t"
// x is pointing to the byte after [ab]
"mov al, [%[x]]\n\t" // Read next byte.
"test al, al\n\t"
"jz NotFound\n\t" // Hit end of string
"cmp al, 'r'\n\t"
"je Phase1\n\t" // Found [^r]+, go look for another [ab]
// ---------------------------------
// Phase3 - Found [ab], and no [^r]+. Now find r.
// x went 1 too far back in Phase1
"dec %[x]\n\t"
// We know there is 1 non-r character after [ab]
"mov %[y], 1\n"
"Phase3:\n\t"
"mov al, [%[x] + %[y]]\n\t" // Read next byte.
"inc %[y]\n\t"
"test al, al\n\t"
"jz NotFound\n\t" // End of string.
"cmp al, 'r'\n\t"
"jne Phase3\n\t"
// Found +r.
"sub %[x], %[s]\n\t" // Set x to offset
"jmp Done\n"
"NotFound:\n\t"
"mov %[y], -1\n"
"Done:"
: [x] "=&r" (x), [y] "=&r" (y)
: [s] "r" (s)
: "eax", "cc", "memory");
主要变化是:
- 假设代码是用
-masm=intel
. 编译的
- 从
"=r"
更改为 "=&r"
。这保证 x
、y
和 s
最终都在不同的寄存器中。
- 使用符号名称。我们可以使用名称
%[x]
. 而不是将 x
称为 %0
- 由于此代码读取内存并修改标志,我添加了 "cc" 和 "memory" clobbers。
- 删除不需要的
volatile
.
这破坏了更少的寄存器(只有 eax)。虽然使用寄存器不是 'bad'(没有它们很难做很多事情),但您在 asm
中保留使用的越多,编译器在调用您的函数之前释放这些寄存器的工作就越多代码。由于 x
、y
和 s
在寄存器中 已经 (由于 "r"
),使用它们可以简化代码。
我意识到这不符合 'answer' 的条件,因为作业要求您使用教师提供的特定格式。然而,由于我觉得使用内联汇编是一种糟糕的学习 asm 的方法,所以我想展示一下如果你将它写成纯 asm 会是什么样子。试图把这个塞进另一个(已经很长的)答案中似乎不太合适。
相反,我建议使用 2 个文件。第一个是纯C代码:
#include <stdio.h>
extern int __cdecl DoRegEx(const char *s, int *startpos);
int main(void)
{
const char *s = "fqr b qabxx xryc pqr";
int startpos, len;
len = DoRegEx(s, &startpos);
printf("%d, %d\n", startpos, len);
return 0;
}
read/maintain 这比您最终使用内联汇编要容易得多。但更重要的是,这是 asm 文件:
# foo2.s - Searches for regex "[ab][^r]+r" in string
#
# Called from c with:
#
# extern int __cdecl DoRegEx(const char *s, int *startpos);
#
# On input:
#
# [esp+4] is s
# [esp+8] is pointer to startpos.
#
# On output:
#
# startpos is the (zero based) offset into (s) where match begins.
# Length of match (or -1 if match not found) is returned in eax.
#
# __cdecl allows the callee (that's us) to modify any of EAX, ECX,
# and EDX. All other registers must be returned unchanged.
#
# Use intel syntax
.intel_syntax noprefix
# export our symbol (note __cdecl prepends an underscore to names).
.global _DoRegEx
# Start code segment
.text
_DoRegEx:
mov ecx, [esp+4] # Load pointer to (s)
Phase1:
mov dl, [ecx] # Read next byte
test dl, dl
jz NotFound # Hit end of string
inc ecx # Move to next byte
cmp dl, 'a' # Check for 'a'
je Phase2
cmp dl, 'b' # Check for 'b'
jne Phase1
... blah blah blah ...
mov edx, [esp+8] # get pointer to startpos
mov DWORD PTR [edx], ecx # write startpos
ret
您可以使用 gcc -m32 -o foo.exe foo1.c foo2.s
.
同时编译+link 两个文件
如果您最终以使用汇编程序为生,那么它看起来比您使用 gcc 的扩展 asm(即使在最好的时候也很丑陋)所看到的更可能是这样的。它还涉及常见的现实世界概念,例如从堆栈读取参数、保存寄存器和使用汇编程序指令(.text、.global 等)。将其内联到 C 中时,这些内容大部分对您是隐藏的,但它们是使用和理解汇编语言的基本组成部分。
FWIW.
PS 你的代码运行了吗?如果其他答案提供了足够的信息来创建您的程序,请不要忘记 'accept' 它。如果您再次遇到困难,请编辑您的原始 post 以添加您当前的代码,并包括对仍然无法正常工作的部分的描述。
我的汇编代码需要帮助。我需要使用编写代码,它会找到适合我的正则表达式的范围。
我的正则表达式:[ab][^r]+r
,所以首先我要查找是否有 'a' 或 'b',然后跳转到 'Start' 部分。现在我有一个问题如何只保存这封信的第一次出现。
程序应显示:5, 10
- 这意味着匹配字符串从第 5 个位置开始,长度为 10。我想保存在 'ecx'
和 'edx'
注册表中的程序结果(或者我可以简化它吗?)
我将不胜感激所有建议和帮助:)
这是一个代码:
#include <stdio.h>
int main(void)
{
char *s = "fqr b qabxx xryc pqr"; // string length= 22, first occurence: 5 position, second one: 9
int x, y;
asm volatile (
".intel_syntax noprefix;"
"mov eax, %2;"
"xor ecx, ecx;"
"xor edx, edx;"
"lea ebx, [eax];"
"loop:"
"mov al, [ebx];"
"or al, al;"
"jz Finish;"
"inc edx;"
"cmp al, 'a';"
"je Start;"
"cmp al, 'b';"
"je Start;"
"jmp JumpNext;"
"Start:"
"mov ecx, edx;"
"jmp loop;"
"JumpNext:"
"inc ebx;"
"jmp loop;"
"Finish:"
"mov %0, ecx;"
"mov %1, edx;"
".att_syntax prefix;"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);
printf("%d, %d \n", x, y);
return 0;
}
编辑:这是完成的代码:
#include <stdio.h>
int main(void)
{
char *s = "fqr b qabxx xryc pqr";
int x, y;
asm volatile (
".intel_syntax noprefix;"
"xor ecx, ecx;" // First occurrence of letter 'a' or 'b'
"mov edx, 1;" // Matching string length
"lea ebx, [%2];"
"loop:"
"mov al, [ebx];"
"cmp al, 0;"
"jz ThereisNoChars;"
"cmp al, 'a';"
"je FoundAorB;"
"cmp al, 'b';"
"je FoundAorB;"
"inc ecx;"
"inc ebx;"
"jmp loop;"
"FoundAorB:"
"inc ebx;"
"inc edx;"
"mov al, [ebx];"
"or al, al;"
"jz ThereisNoChars;"
"cmp al, 'r';"
"je isRafterAorB;"
"jne ThereIsNoR;"
"isRafterAorB:"
"mov edx, 1;"
"inc ebx;"
"inc ecx;"
"jmp loop;"
"ThereIsNoR:"
"inc ebx;"
"inc edx;"
"mov al,[ebx];"
"or al, al;"
"jz ThereisNoChars;"
"cmp al, 'r';"
"je Finish;"
"jmp ThereIsNoR;"
"ThereisNoChars:"
"mov ecx, 0;"
"mov edx, 0;"
"jmp Finish;"
"Finish:"
"mov %0, ecx;"
"mov %1, edx;"
".att_syntax prefix;"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);
printf("%d, %d \n", x, y);
return 0;
}
显示预期结果(5, 10)。这意味着,匹配的正则表达式从第 5 个位置开始,长度为 10
首先,你对自己的要求有点不清楚。当我第一次阅读您的 post 时,看起来您正试图用汇编程序 (
其次,你应该和
第三,有人应该和你的导师谈谈他的作业。使用 gcc 的 "inline asm" 来教授 asm 可能是最难的方法。他讨厌他的学生吗?我对他提供的 "template" 印象不深,(显然?)您不允许更改。我可以在这里看到至少六处我要更改的东西。
第四,你说正则表达式字符串“[ab][^r]+r”应该为"fqr b qabxx xryc pqr"打印出5, 10
。我不确定那怎么可能。匹配从(从零开始)5 开始,但不在位置 10 结束:
1 2
0123456789012345678901
fqr b qabxx xryc pqr
^ ^
start end
结尾是位置 15。匹配字符串 (b qabxx xr
) 的长度为 11 个字符,因此显然您不是在寻找长度。第二个 'start' 出现在位置 8,第三个出现在位置 9,并且还有多个可能的端点。 None 其中解释了您应该在哪里获得“10”。我假设你的意思是 5, 15
.
综上所述,处理 [ab][^r]+r
基本上分为 3 个部分:
[ab]
查找 'a' 或 'b'。如果找不到字符串,则在遇到字符串结尾时出错并退出。[^r]+
如果 (1) 后面的字母是 'r',转到 1.r
遍历字符串的其余部分并在下一个成功退出 'r',或者在字符串末尾错误退出。
如果您不明白为什么会有这些部分,请尝试使用 https://regex101.com/r/E3nI1F/1(它可以让您尝试各种正则表达式搜索字符串以查看它找到的内容)。
查看您当前的代码,我认为您没有正确处理 (2) 或 (3)(实际上,我认为您根本没有处理它们)。虽然我会在您的代码中更改其他内容,但也许调整应该等到事情正常工作。
考虑到这是家庭作业,我不想只 post 编写我的代码。如果你只是 copy/paste 我的作品,你不会学到任何东西。
如果您想在添加 2 和 3 的作业后编辑您的问题,我可以再次审核或提供更多建议。如果你post一份工作副本,我可以分享我的,我们可以比较它们。
------------ 编辑 1 --------------
my teacher does not seem to hate us
哦?考虑这段代码(你的简化版本):
asm volatile (
"xor %0, %0;"
"mov %1, %2"
:"=r" (x), "=r" (y)
:"r" (s));
看起来很简单,对吧?将 x
归零,然后将 s
复制到 y
。但是,由于一些叫做 "early clobber" 的东西(参见 https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html 上的 '&'
),gcc 在优化时 可能 (不保证)会选择%0 和 %2(或者可能是 %1 和 %2)使用相同的寄存器。因此,当您将 %0 归零时,您也可以将 %2 归零。
这可以通过添加 & 符号来解决,以确保没有重叠:
:"=&r" (x), "=&r" (y)
但是你怎么会知道这个呢?知道这个细节并不能帮助你学习汇编程序。这只是关于 gcc 的内联 asm 工作原理的一个奇怪的怪癖。如果你正在编写一个实际的 asm 例程(这是我推荐的),你永远不需要知道这一点。
如果您使用符号名称,这不是更容易阅读吗?
asm volatile (
"xor %[x], %[x];"
"mov %[y], %[s]"
: [x] "=&r" (x), [y] "=&r" (y)
: [s] "r" (s));
我觉得它更容易阅读。但这是另一件与汇编语言无关的事情。这只是一个关于如何在使用 gcc 时将内联 asm 推入 c 代码的技巧(你应该 almost never 做的事情)。
还有什么?此模板的其他一些问题:volatile
限定符不属于此处。它缺少 "cc"
破坏。还有 "memory"
破坏。你最终破坏了比你需要的更多的寄存器。哦,为什么不直接告诉人们使用 -masm=intel
编译并避免使用“.intel_syntax noprefix;”和“.att_syntax 前缀;”垃圾(还有更多 gcc 怪癖)。
使用汇编语言可以有用。我并不是要说它不是。但是尝试使用 gcc 的内联 asm 充满了怪癖。由于可以从 C 代码调用用纯汇编程序编写的函数,并且由于该方法有 none 个这些问题,我只能得出结论,你被迫这样做是因为你对 him/her 和(s)他讨厌你。
------------ 编辑 2 --------------
既然你已经 post 编辑了工作代码(假设你已经修复了 "arb r"
),让我提供我的代码:
#include <stdio.h>
int main(int argc, char *argv[])
{
const char *s = "fqr b qabxx xryc pqr"; // Succeeds with 5,11
int x, y;
// Assumes s is not NULL.
// Return y=-1 on not found.
asm volatile (
".intel_syntax noprefix\n\t"
"lea ebx, [%2-1]\n\t" // ebx is pointer to next character.
"mov ecx, %2\n\t" // Save this since we aren't using earlyclobber...
"mov %1, -1\n" // ...so at this point, %2 might be dead.
// Note that ebx starts at s-1.
"Phase1:\n\t"
"inc ebx\n\t"
"mov al, [ebx]\n\t" // Read next byte.
"test al, al\n\t"
"jz Done\n\t" // End of string.
// Check for [ab]
"cmp al, 'a'\n\t"
"je Phase2\n\t"
"cmp al, 'b'\n\t"
"jne Phase1\n"
// Phase 2 - Found [ab], check for [^r]+
"Phase2:\n\t"
"mov al, byte ptr [ebx+1]\n\t"
"test al, al\n\t"
"jz Done\n\t" // End of string.
"cmp al, 'r'\n\t"
"je Phase1\n\t" // Found [^r]+, go look for another [ab]
"mov %0, ebx\n\t"
// Found [ab], and no [^r]+. Find r.
"mov ebx, 1\n"
"Phase3:\n\t"
"mov al, [%0 + ebx]\n\t" // Read next byte.
"inc ebx\n\t"
"test al, al\n\t"
"jz Done\n\t" // End of string.
"cmp al, 'r'\n\t"
"jne Phase3\n\t"
// Found r.
"sub %0, ecx\n\t" // Set (x)
"mov %1, ebx\n"
"Done:\n"
".att_syntax prefix"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);
printf("%d, %d \n", x, y);
return 0;
}
它更短,不需要那么多寄存器(没有 edx)。虽然它可以再调整一点,但它是解决家庭作业问题的可靠方法。
如果让你改一下框架,可能会好一点:
// Returns y = -1 if no regex match is found.
__asm__ (
// ---------------------------------
// Phase1 - look for [ab]
"mov %[x], %[s]\n" // Pointer to next char to read
"Phase1:\n\t"
"mov al, [%[x]]\n\t" // Read next byte
"test al, al\n\t"
"jz NotFound\n\t" // Hit end of string
"inc %[x]\n\t"
"cmp al, 'a'\n\t"
"je Phase2\n\t"
"cmp al, 'b'\n\t"
"jne Phase1\n"
// ---------------------------------
// Phase2 - Found [ab], Check for [^r]+
"Phase2:\n\t"
// x is pointing to the byte after [ab]
"mov al, [%[x]]\n\t" // Read next byte.
"test al, al\n\t"
"jz NotFound\n\t" // Hit end of string
"cmp al, 'r'\n\t"
"je Phase1\n\t" // Found [^r]+, go look for another [ab]
// ---------------------------------
// Phase3 - Found [ab], and no [^r]+. Now find r.
// x went 1 too far back in Phase1
"dec %[x]\n\t"
// We know there is 1 non-r character after [ab]
"mov %[y], 1\n"
"Phase3:\n\t"
"mov al, [%[x] + %[y]]\n\t" // Read next byte.
"inc %[y]\n\t"
"test al, al\n\t"
"jz NotFound\n\t" // End of string.
"cmp al, 'r'\n\t"
"jne Phase3\n\t"
// Found +r.
"sub %[x], %[s]\n\t" // Set x to offset
"jmp Done\n"
"NotFound:\n\t"
"mov %[y], -1\n"
"Done:"
: [x] "=&r" (x), [y] "=&r" (y)
: [s] "r" (s)
: "eax", "cc", "memory");
主要变化是:
- 假设代码是用
-masm=intel
. 编译的
- 从
"=r"
更改为"=&r"
。这保证x
、y
和s
最终都在不同的寄存器中。 - 使用符号名称。我们可以使用名称
%[x]
. 而不是将 - 由于此代码读取内存并修改标志,我添加了 "cc" 和 "memory" clobbers。
- 删除不需要的
volatile
.
x
称为 %0
这破坏了更少的寄存器(只有 eax)。虽然使用寄存器不是 'bad'(没有它们很难做很多事情),但您在 asm
中保留使用的越多,编译器在调用您的函数之前释放这些寄存器的工作就越多代码。由于 x
、y
和 s
在寄存器中 已经 (由于 "r"
),使用它们可以简化代码。
我意识到这不符合 'answer' 的条件,因为作业要求您使用教师提供的特定格式。然而,由于我觉得使用内联汇编是一种糟糕的学习 asm 的方法,所以我想展示一下如果你将它写成纯 asm 会是什么样子。试图把这个塞进另一个(已经很长的)答案中似乎不太合适。
相反,我建议使用 2 个文件。第一个是纯C代码:
#include <stdio.h>
extern int __cdecl DoRegEx(const char *s, int *startpos);
int main(void)
{
const char *s = "fqr b qabxx xryc pqr";
int startpos, len;
len = DoRegEx(s, &startpos);
printf("%d, %d\n", startpos, len);
return 0;
}
read/maintain 这比您最终使用内联汇编要容易得多。但更重要的是,这是 asm 文件:
# foo2.s - Searches for regex "[ab][^r]+r" in string
#
# Called from c with:
#
# extern int __cdecl DoRegEx(const char *s, int *startpos);
#
# On input:
#
# [esp+4] is s
# [esp+8] is pointer to startpos.
#
# On output:
#
# startpos is the (zero based) offset into (s) where match begins.
# Length of match (or -1 if match not found) is returned in eax.
#
# __cdecl allows the callee (that's us) to modify any of EAX, ECX,
# and EDX. All other registers must be returned unchanged.
#
# Use intel syntax
.intel_syntax noprefix
# export our symbol (note __cdecl prepends an underscore to names).
.global _DoRegEx
# Start code segment
.text
_DoRegEx:
mov ecx, [esp+4] # Load pointer to (s)
Phase1:
mov dl, [ecx] # Read next byte
test dl, dl
jz NotFound # Hit end of string
inc ecx # Move to next byte
cmp dl, 'a' # Check for 'a'
je Phase2
cmp dl, 'b' # Check for 'b'
jne Phase1
... blah blah blah ...
mov edx, [esp+8] # get pointer to startpos
mov DWORD PTR [edx], ecx # write startpos
ret
您可以使用 gcc -m32 -o foo.exe foo1.c foo2.s
.
如果您最终以使用汇编程序为生,那么它看起来比您使用 gcc 的扩展 asm(即使在最好的时候也很丑陋)所看到的更可能是这样的。它还涉及常见的现实世界概念,例如从堆栈读取参数、保存寄存器和使用汇编程序指令(.text、.global 等)。将其内联到 C 中时,这些内容大部分对您是隐藏的,但它们是使用和理解汇编语言的基本组成部分。
FWIW.
PS 你的代码运行了吗?如果其他答案提供了足够的信息来创建您的程序,请不要忘记 'accept' 它。如果您再次遇到困难,请编辑您的原始 post 以添加您当前的代码,并包括对仍然无法正常工作的部分的描述。