![](/img/trans.png)
[英]Compiling and Running ARM Assembly thumb-2 in Rasperry pi?
[英]Arm Assembly Rasperry-Pi: Converting a string to Upper case
我正在开发一个用户输入姓名的程序,该程序应将所有小写字母转换为大写:
我正在使用 %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 数字格式。
有什么办法可以将整个单词转换为大写?
这可能会奏效。 我目前没有任何 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 的代码,只是我假设了一个以空字符结尾的字符串而不是一个pascal类型的字符串。 要叫它,
ldr r0,=string
bl upperString
上面是根据.asciz
伪操作的 'C' 格式的 'NULL'(零值)终止的ASCII字符串。 字符串编码的另一种格式是 Pascal 类型。 一个 Pascal 字符串比喻为int size; char data[size]
int size; char data[size]
并且没有空终止符。 pascal 字符串的循环机制会有所不同,但核心( xor 0x20
或sub 'a' - 'A'
)对于 ASCII 编码是相同的。
一些字符串编码是不同的。 对于固定宽度的字符串,常量会改变。 一些字符串使用转义机制,每个“字形”或字母由不同数量的数据表示。 在这种情况下,“步进”汇编器会发生变化。
最后,对于'C' 库,您经常想知道,这是一个数字,这是一个标点符号,等等。在这些情况下,可以为每个具有该字符属性的字符表建立索引。 如果 'Upper' 和 'Lower' 大小写的编码不是连续范围,您也可以使用此表方法。
希望变体部分对非“剪切和粘贴”程序员有用。
检测一个字符是否在'a'
和'z'
只需要一个sub
和一个cmp
来进行范围检查。 (有关详细信息,请参阅^= 32 背后的想法是什么,将小写字母转换为大写字母,反之亦然? )
除了最初是小写字母的字符外,我们可以保留所有字符不变。 在 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
您可以将upperString
标签放在循环中间,而不是从b .Lentry
开始,因此使用bx upperString
在循环中间开始调用它。 (通常函数标签位于函数的顶部,但如果不是,则任何假设都会将前面的代码视为不同函数的一部分的工具)。
重新安排循环以在底部具有条件分支(并且没有无条件分支)称为“循环旋转”优化; 这就是为什么我们必须从中间开始。
不幸的是,Thumb 模式cbnz
只能向前跳转,因此您不能将其用作循环分支。
这个版本的函数在循环中的指令比@artless noise 的少(7 对 10),但它们每次都运行。 这对分支预测很有好处,但在不太依赖它的简单低端 CPU 上可能更糟。
这可以在 ARM 或 Thumb-2 中组装(例如使用arm-none-eabi-gcc -c -mcpu=cortex-m3
),但不适用于只有 Thumb-1 的 CPU。 (例如皮质-m0)。
sub
的目标寄存器与源寄存器不同,并且立即数很大,不适合单个狭窄的 16 位指令,既不是 subs 也不是 sub。 使用[r0, #-1]
寻址模式的strb
也不strb
。
sub/cmp 在 2 条指令中完成工作。 对于某些条件,您可以使用cmp
/ cmpXX
(带有一些谓词)以某种有用的方式设置标志。 但是在这里,即使r1<'a'
, cmp r1, #'a'
/ cmphs r1, #'z'
也会使 LS 条件成立。 因此,指令之一必须是rsbs
进行反向减法,或者您需要寄存器中的常量之一,以便您可以执行cmp r1, #'a'
/ cmphs r2, r1
以获得一致的标志条件而无需修改任何寄存器.
您当然可以使用 NEON SIMD 指令更快地执行此操作,一次 8 或 16 个字节,特别是如果您知道长度而不是还必须搜索终止的 0 字节。 有关 x86 SSE2 版本,请参阅将 C++ 中的字符串转换为大写。
这是基本算法:
for (int idx = 0; idx < len; ++idx)
if (str [idx] >= 'A' && str [idx] <= 'Z')
str [idx] += 'a' - 'A';
它有几个你没有的部分。 逐个字符扫描字符串。 检查大写字母。 添加(而不是减去)小写/大写偏移量。
请注意,这通常不适用于 Unicode。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.