繁体   English   中英

Arm Assembly 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 0x20sub '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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM