我正在处理一个用户输入他的名字的程序,程序应该将所有小写字母转换为大写字母:
我使用%s格式来读取字符串:
.text
ldr r0,=msj
bl printf
ldr r0,=format
ldr r1,string
bl scanf
.data
.align 2
msj: .asciz "Enter you name: "
format: .asciz "%s"
string: .asciz ""
我尝试将32减去每个字符,但我认为字符串不是ascii数字格式。
有什么方法可以将整个单词转换成大写字母?
答案 0 :(得分:2)
这可能有用。我目前没有任何ARM资料。
; call with address of string in 'R0'.
upperString:
1: ldrb r1,[r0],#1
tst r1 ; finished string with null terminator?
bxeq lr ; then done and return
cmp r1,#'a' ; less than a?
blo 1b ; then load next char.
cmp r1,#'z' ; greater than z?
bhi 1b ; then load next char.
; Value to upper case.
sub r1,r1,#('a' - 'A') ; subtract 32.
strb r1,[r0,#-1] ; put it back to memory.
b 1b ; next character.
至少这是一个很好的起点。这就像wallyk's代码,除了我假设以null结尾字符串而不是 pascal 类型字符串。要打电话,
ldr r0,=string
bl upperString
答案 1 :(得分:1)
检测一个字符是否在'a'
和'z'
之间只需要一个sub
和一个cmp
来做范围检查。 (有关详细信息,请参阅 What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?。)
我们可以保留所有字符不变,除了最初是小写字母的字符。在 ARM 模式下,我们可以轻松地预测存储(如果条件为假,则充当 NOP)。假设 CPU 有效地处理这个,它不会弄脏没有小写字符的字符串的缓存。 (@artless noise 的回答也是这样做的,在到达商店之前跳回循环的顶部。)
.syntax unified
@ call this with address of string in R0
upperString_ARM_mode:
b .Lentry @ start in the middle of the loop. Or put upperString: there instead of here.
.Lloop: @ do {
sub r2, r1, #'a'
bic r1, #0x20 @ clear the lower-case bit in the original
cmp r2, #'z'-'a' @ set flags
it ls @ For Thumb2 compat; assembles to nothing in ARM mode
strbls r1, [r0, #-1] @ strb with LS predicate (Lower-or-Same unsigned <=)
@ store upcased version if (c-'a') <=(unsigned) length of alphabet
.Lentry:
ldrb r1, [r0],#1 @ zero-extending byte load (with post-increment addressing)
tst r1, r1
bne .Lloop @ }while( *p != 0 )
bx lr @ return. (R0 pointing at terminating 0 byte)
@@@ UNTESTED, except for checking that it assembles for both ARM and Thumb-2
@@@ Doesn't work for Thumb-1
您可以将 b .Lentry
标签放在循环中间而不是从 upperString
开始,因此使用 bx upperString
调用它在循环中间开始. (通常函数标签位于函数的顶部,但如果不是,则任何假设都会将前面的代码视为不同函数的一部分的工具)。
重新安排循环以在底部有条件分支(并且没有无条件分支)称为“循环轮换”优化;这就是为什么我们必须从中间开始。
不幸的是,Thumb 模式 cbnz
只能向前跳转,因此您不能将其用作循环分支。
这个版本的函数在循环中的指令比@artless noise 的少(7 对 10),但它们每次都运行。这对分支预测很有好处,但在不太依赖它的简单低端 CPU 上可能更糟。
这可以在 ARM 或 Thumb-2 中组装(例如使用 arm-none-eabi-gcc -c -mcpu=cortex-m3
),但不适用于只有 Thumb-1 的 CPU。 (例如 cortex-m0)。
sub
的目标寄存器与源寄存器不同,并且有一个很大的立即数,不适合单个窄的 16 位指令,既不是 subs 也不是 sub。使用 strb
寻址模式的 [r0, #-1]
也不行。
sub/cmp 通过 2 条指令完成工作。对于某些条件,您可以使用 cmp
/ cmpXX
(带有一些谓词)以某种有用的方式设置标志。但在这里,即使 cmp r1, #'a'
,cmphs r1, #'z'
/ r1<'a'
也会使 LS 条件为真。因此,指令之一必须是 rsbs
以进行反向减法,或者您需要寄存器中的常量之一,以便您可以执行 cmp r1, #'a'
/ cmphs r2, r1
以获得一致的标志条件,而无需修改任何寄存器。
您当然可以使用 NEON SIMD 指令更快地完成此操作,一次 8 或 16 个字节,特别是如果您知道长度而不是还必须搜索终止的 0 字节。有关 x86 SSE2 版本,请参阅 Convert a String In C++ To Upper Case。
答案 2 :(得分:0)
这是必不可少的算法:
for (int idx = 0; idx < len; ++idx)
if (str [idx] >= 'A' && str [idx] <= 'Z')
str [idx] += 'a' - 'A';
它有几个你没有的部分。逐个字符地扫描字符串。检查大写字母。添加(不减去)小写/大写偏移量。
请注意,这通常不适用于Unicode。