装配和缓冲器 如何
Assembly and Buffers How to
我很困惑,遇到了障碍。 class 的作业要求我执行以下操作。
- 问候用户
- 输入提示
- 将字符串转换为全部大写
- 使用转换后的字符串向用户显示消息
我对 1 和 2 没有问题,需要时我可以找出将小写字母转换为大写字母的循环,例如;
cmp , %ah
jg Subtract
call Loop
Subtract:
sub , %ah
mov %ah, back to the array
ret
这可能不是最好的方法,但一旦我弄清楚了这个数组和缓冲区,我就能弄清楚。所以教授让我们做事的方式涉及使用他的图书馆。为了获得用户输入,代码如下所示:
.data
Intro:
.ascii "Hey enter in your what you want converted.\n[=11=]"
Task:
.space 5 #This is the buffer that is supposed to limit what the user can enter.... I'm very confused about how to make this work
.text
.global _start
_start:
mov $Intro, %rax
call PrintCString #from the Prof's library
mov $Task, %rbx
call ScanCString
这是他对使用 ScanCString 的评价
输入 = rax, rbx
Notes = 扫描以 null 结尾的字符串并将其存储到地址 %rax 中。寄存器 %rbx 必须包含可以读取的最大字符数(缓冲区的大小)。
我相信您可以从上面收集到的我的想法是移动每个字符,确定它是大写还是小写并相应地进行调整。我将 运行 通过一个循环,然后将其吐回给用户。
这是我到目前为止的所有内容,请不要介意我的测试内容太乱了。任何帮助将不胜感激。
.data
Intro:
.ascii "\nYup... Mr. Meekseeks here to help. What ya want me to do?\n\n[=12=]"
Task:
.space 5
NewLine:
.ascii "\n\n[=12=]"
Goodbye:
.ascii "\nYou got it buddy I'll get right on doing [=12=]"
Test:
.ascii "\nYou made it to the first loop\n[=12=]"
Test2:
.ascii "\nYou made it past the first compare\n[=12=]"
Test3:
.ascii "\nYou added 1 to the pointer\n[=12=]"
Test4:
.ascii "\nHere's the string length [=12=]"
.text
.global _start
_start:
mov $Intro, %rax #start with greeting the user
call PrintCString #Print the greeting to the user
mov [=12=], %rax
mov Task(,8), %rbx #move the buffer into RBX, prep for input
call ScanCString #User Input
# mov %rax, Task
# mov %rax, %rdx #Move the message so it's not destroyed
call LengthCString #Determine loop limit
mov [=12=], %rdi #set pointer
mov %rax, %rcx #set counter to zero
mov Task(%rdi), %rax
call PrintCString
mov $Test4, %rax
call PrintCString
mov %rcx, %rax
call PrintInt
mov %rax, %rcx
mov $Test, %rax
call Loop
Loop:
mov $Test2, %rax
call PrintCString
add , %rdi
mov $Test3, %rax
call PrintCString
cmp %rdi, %rcx
je Closing
call Loop
# movb %rax, %rdx
# mov %rcx, %rax
# call PrintInt
# call PrintCString
# call Ending
#Adding:
# Greeting:
# mov $Intro, %rax
# call PrintCString
# ret
Closing:
mov $Goodbye, %rax
call PrintCString
call Ending
Ending:
mov $NewLine, %rax
call PrintCString
call EndProgram
ScanCString Input = rax, rbx Notes = Scans a null-terminated string and stores it into the address %rax. The register %rbx must contain the maximum number of characters that can be read (the size of the buffer).
看起来你应该这样称呼它:
mov $Task, %rax # set rax to point to the buffer in memory
mov , %rbx # size of buffer (5 bytes)
call ScanCString
你的:
mov [=11=], %rax
将 rax
设置为零(ScanCString 会将其用作内存指针,因此它将引用 "null" 指针并可能导致崩溃,或者如果它对空指针进行了安全测试则提前退出) .
mov Task(,8), %rbx #move the buffer into RBX, prep for input
从内存地址 Task
加载八个字节(实际上更可能是语法错误,我认为 (,8)
不会解析,但我不是 AT&T 语法专家......如果我愿意的话猜想它就像 Task(<noreg>,<noreg>,8)
,提供比例因子 8 是没有意义的,因为那里没有索引寄存器,所以逻辑上我会将其评估为零偏移量,添加到任务地址,并使用它来加载 8字节。
由于 Task:
后面跟着 .space 5
,这意味着将从 space 保留中读取 5 个未定义的字节,然后读取接下来的 3 个字节以命中完整 8 个字节(rbx
大小)。
call ScanCString #User Input
# mov %rax, Task
这会将 8 个字节的 rax
值存储到 Task:
指向的内存中(其中仅保留 5 个字节,因此它将覆盖超出的 3 个字节)。
# mov %rax, %rdx #Move the message so it's not destroyed
rdx = rax
...不清楚 ScanCString
return 在 rax
中的作用,所以谁知道 memory/rdx 中存储了什么以上两条说明。
call LengthCString #Determine loop limit
这可能需要指向 rax
中的内存指针,所以提前 mov $Task, %rax
对我来说似乎合乎逻辑,但你没有 post LengthCString
的文档.
...
jg Subtract
...
Subtract:
...
ret
jg
是跳转,没有把return地址存入栈,所以最后的ret
感觉不对,ret
需要对应的call
配对(除非你知道自己在做什么,并且你通过 call
以外的其他方式在堆栈中准备 returning 地址,但这不是初学者的主题)。
Task:
.space 5 #This is the buffer that is supposed to limit
# what the user can enter....
# I'm very confused about how to make this work
不,这只是在当前代码段中保留了 5 个字节 (.data
)。它不以任何方式限制代码,它甚至不定义该内存的任何内容,因此在执行时它可能包含任何内容(我认为 .data
通常在大多数平台上归零,因此它会在大多数目标平台上包含五个零字节(下次指定一个)。
5
只是在源代码中,它不是生成的机器代码的一部分,因此汇编器知道它,但代码本身不知道。如果你想 DRY,你应该定义一些像 buffer_length = 5
这样的常量(不确定 AT&T 语法),然后在数据部分 Task: .space buffer_length
和扫描调用 mov $buffer_length, %ebx
之前做(ebx
就足够了,因为你可能不会使用 4+GB 的长度,并且设置 ebx
部分只会清除 rbx
的剩余高 32 位),这样魔术常数 5
将仅保留在源代码中的一个位置。
总的来说,到目前为止,您似乎还没有完全理解什么是寄存器、什么是计算机内存、CPU 如何与它交互等等。尝试阅读一些有解释的书籍或教程,以及你的课程笔记。复制已经存在的代码并调整它,直到它产生你期望的输出,对于高级语言比汇编更好,在汇编中更好地尝试完全理解事情是如何工作的。好的部分是,计算机实际上是非常简单的机器,一个以确定性方式运行的状态自动机,每个周期只有很少的可能指令要执行,所以理解它并不难,只是不要使用人类expectations/logic 在上面,它是计算机器,而不是高级语言,应该是 written/read 人类和人类。
此外,在编写汇编代码时,必须始终验证每条指令在调试器中是否按预期工作,方法是单步执行它们,并检查指令产生的所有状态变化。如果出现一些差异,请查阅说明参考指南,以确保您完全理解特定说明的作用。如果您不想深入研究英特尔的原始 pdf(可从英特尔网站免费获得),网络上有一些简短版本,如 http://www.felixcloutier.com/x86/。不要根据它的名字来判断指令的作用,臭名昭著的例子(和关于 SO 的问题)例如 mul
和 div
,它们根本不符合普遍的期望。
此外,AT&T 语法更像 IMO "machine-like",非常适合机器解析,而且非常 precise/exact,但与宽松的 Intel 语法相比,IMO 更难 write/read 随便.
尤其是像这样的东西:
mov $Task, %rax # set rax to point to the buffer in memory
mov Task, %rax # set rax to 64b value from memory at Task addres
对机器解析非常有意义, $
区分立即值和内存引用,但是当你想要内存地址时,尽量不要忘记手写 $
,并且不满足。
我很困惑,遇到了障碍。 class 的作业要求我执行以下操作。
- 问候用户
- 输入提示
- 将字符串转换为全部大写
- 使用转换后的字符串向用户显示消息
我对 1 和 2 没有问题,需要时我可以找出将小写字母转换为大写字母的循环,例如;
cmp , %ah
jg Subtract
call Loop
Subtract:
sub , %ah
mov %ah, back to the array
ret
这可能不是最好的方法,但一旦我弄清楚了这个数组和缓冲区,我就能弄清楚。所以教授让我们做事的方式涉及使用他的图书馆。为了获得用户输入,代码如下所示:
.data
Intro:
.ascii "Hey enter in your what you want converted.\n[=11=]"
Task:
.space 5 #This is the buffer that is supposed to limit what the user can enter.... I'm very confused about how to make this work
.text
.global _start
_start:
mov $Intro, %rax
call PrintCString #from the Prof's library
mov $Task, %rbx
call ScanCString
这是他对使用 ScanCString 的评价 输入 = rax, rbx Notes = 扫描以 null 结尾的字符串并将其存储到地址 %rax 中。寄存器 %rbx 必须包含可以读取的最大字符数(缓冲区的大小)。
我相信您可以从上面收集到的我的想法是移动每个字符,确定它是大写还是小写并相应地进行调整。我将 运行 通过一个循环,然后将其吐回给用户。
这是我到目前为止的所有内容,请不要介意我的测试内容太乱了。任何帮助将不胜感激。
.data
Intro:
.ascii "\nYup... Mr. Meekseeks here to help. What ya want me to do?\n\n[=12=]"
Task:
.space 5
NewLine:
.ascii "\n\n[=12=]"
Goodbye:
.ascii "\nYou got it buddy I'll get right on doing [=12=]"
Test:
.ascii "\nYou made it to the first loop\n[=12=]"
Test2:
.ascii "\nYou made it past the first compare\n[=12=]"
Test3:
.ascii "\nYou added 1 to the pointer\n[=12=]"
Test4:
.ascii "\nHere's the string length [=12=]"
.text
.global _start
_start:
mov $Intro, %rax #start with greeting the user
call PrintCString #Print the greeting to the user
mov [=12=], %rax
mov Task(,8), %rbx #move the buffer into RBX, prep for input
call ScanCString #User Input
# mov %rax, Task
# mov %rax, %rdx #Move the message so it's not destroyed
call LengthCString #Determine loop limit
mov [=12=], %rdi #set pointer
mov %rax, %rcx #set counter to zero
mov Task(%rdi), %rax
call PrintCString
mov $Test4, %rax
call PrintCString
mov %rcx, %rax
call PrintInt
mov %rax, %rcx
mov $Test, %rax
call Loop
Loop:
mov $Test2, %rax
call PrintCString
add , %rdi
mov $Test3, %rax
call PrintCString
cmp %rdi, %rcx
je Closing
call Loop
# movb %rax, %rdx
# mov %rcx, %rax
# call PrintInt
# call PrintCString
# call Ending
#Adding:
# Greeting:
# mov $Intro, %rax
# call PrintCString
# ret
Closing:
mov $Goodbye, %rax
call PrintCString
call Ending
Ending:
mov $NewLine, %rax
call PrintCString
call EndProgram
ScanCString Input = rax, rbx Notes = Scans a null-terminated string and stores it into the address %rax. The register %rbx must contain the maximum number of characters that can be read (the size of the buffer).
看起来你应该这样称呼它:
mov $Task, %rax # set rax to point to the buffer in memory
mov , %rbx # size of buffer (5 bytes)
call ScanCString
你的:
mov [=11=], %rax
将 rax
设置为零(ScanCString 会将其用作内存指针,因此它将引用 "null" 指针并可能导致崩溃,或者如果它对空指针进行了安全测试则提前退出) .
mov Task(,8), %rbx #move the buffer into RBX, prep for input
从内存地址 Task
加载八个字节(实际上更可能是语法错误,我认为 (,8)
不会解析,但我不是 AT&T 语法专家......如果我愿意的话猜想它就像 Task(<noreg>,<noreg>,8)
,提供比例因子 8 是没有意义的,因为那里没有索引寄存器,所以逻辑上我会将其评估为零偏移量,添加到任务地址,并使用它来加载 8字节。
由于 Task:
后面跟着 .space 5
,这意味着将从 space 保留中读取 5 个未定义的字节,然后读取接下来的 3 个字节以命中完整 8 个字节(rbx
大小)。
call ScanCString #User Input
# mov %rax, Task
这会将 8 个字节的 rax
值存储到 Task:
指向的内存中(其中仅保留 5 个字节,因此它将覆盖超出的 3 个字节)。
# mov %rax, %rdx #Move the message so it's not destroyed
rdx = rax
...不清楚 ScanCString
return 在 rax
中的作用,所以谁知道 memory/rdx 中存储了什么以上两条说明。
call LengthCString #Determine loop limit
这可能需要指向 rax
中的内存指针,所以提前 mov $Task, %rax
对我来说似乎合乎逻辑,但你没有 post LengthCString
的文档.
...
jg Subtract
...
Subtract:
...
ret
jg
是跳转,没有把return地址存入栈,所以最后的ret
感觉不对,ret
需要对应的call
配对(除非你知道自己在做什么,并且你通过 call
以外的其他方式在堆栈中准备 returning 地址,但这不是初学者的主题)。
Task:
.space 5 #This is the buffer that is supposed to limit
# what the user can enter....
# I'm very confused about how to make this work
不,这只是在当前代码段中保留了 5 个字节 (.data
)。它不以任何方式限制代码,它甚至不定义该内存的任何内容,因此在执行时它可能包含任何内容(我认为 .data
通常在大多数平台上归零,因此它会在大多数目标平台上包含五个零字节(下次指定一个)。
5
只是在源代码中,它不是生成的机器代码的一部分,因此汇编器知道它,但代码本身不知道。如果你想 DRY,你应该定义一些像 buffer_length = 5
这样的常量(不确定 AT&T 语法),然后在数据部分 Task: .space buffer_length
和扫描调用 mov $buffer_length, %ebx
之前做(ebx
就足够了,因为你可能不会使用 4+GB 的长度,并且设置 ebx
部分只会清除 rbx
的剩余高 32 位),这样魔术常数 5
将仅保留在源代码中的一个位置。
总的来说,到目前为止,您似乎还没有完全理解什么是寄存器、什么是计算机内存、CPU 如何与它交互等等。尝试阅读一些有解释的书籍或教程,以及你的课程笔记。复制已经存在的代码并调整它,直到它产生你期望的输出,对于高级语言比汇编更好,在汇编中更好地尝试完全理解事情是如何工作的。好的部分是,计算机实际上是非常简单的机器,一个以确定性方式运行的状态自动机,每个周期只有很少的可能指令要执行,所以理解它并不难,只是不要使用人类expectations/logic 在上面,它是计算机器,而不是高级语言,应该是 written/read 人类和人类。
此外,在编写汇编代码时,必须始终验证每条指令在调试器中是否按预期工作,方法是单步执行它们,并检查指令产生的所有状态变化。如果出现一些差异,请查阅说明参考指南,以确保您完全理解特定说明的作用。如果您不想深入研究英特尔的原始 pdf(可从英特尔网站免费获得),网络上有一些简短版本,如 http://www.felixcloutier.com/x86/。不要根据它的名字来判断指令的作用,臭名昭著的例子(和关于 SO 的问题)例如 mul
和 div
,它们根本不符合普遍的期望。
此外,AT&T 语法更像 IMO "machine-like",非常适合机器解析,而且非常 precise/exact,但与宽松的 Intel 语法相比,IMO 更难 write/read 随便.
尤其是像这样的东西:
mov $Task, %rax # set rax to point to the buffer in memory
mov Task, %rax # set rax to 64b value from memory at Task addres
对机器解析非常有意义, $
区分立即值和内存引用,但是当你想要内存地址时,尽量不要忘记手写 $
,并且不满足。