首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ARM组件中的十进制到二进制转换

ARM组件中的十进制到二进制转换
EN

Code Review用户
提问于 2021-02-16 12:29:32
回答 1查看 1.5K关注 0票数 4

我正在“玩”与手臂组装在一个树莓派,阅读各种教程和帖子帮助-不,这不是一个大学作业,我太老了,大学!我很高兴让下面的代码获得一个十进制值(介于0到255之间),并使用scanf()printf()输出相当于屏幕的二进制字符串,但我很想知道那些经验更丰富的人是否会认为这是一个“天真”的解决方案?有什么“陷阱”吗?有什么更好的方法来解决这个问题吗?

我认为应该有一种更好的方法来声明字符串来保存输出,以及字符串是如何从LSB构建的(使用R9)。

代码语言:javascript
复制
/* dectobin2.s                                                  */
/* ------------------------------------------------------------ */
/* Converts a decimal number to binary and outputs to screen    */
/* ============================================================ */

.data
.balign 4
value:      .word   0   
binStr:     .asciz  "00000000"
output:     .asciz  "%d in binary is %s\n"
title:      .asciz  "DECIMAL TO BINARY CONVERTER\n\n"
lr_temp:    .word   0
lr_local:   .word   0
prompt:     .asciz  "Enter a positive value (0-255) > "
pattern:    .asciz  "%d"

.text
@ function to convert given value into a binary string
decToBin:
    @ save the link register
    ldr     r2, =lr_local
    str     lr, [r2]
    @ set data values
    mov     r4, r0              @ value in r4 for processing
    mov     r9, #7              @ LSB position in output string
_loop:
    @ repeated division by 2 2
    movs    r4, r4, lsr #1      @ divide by 2, set carry
    bcs     _odd                @ if carry set, remainder is 1
_even:
    mov     r5, #0              @ remainder of 0
    b       _toString
_odd:
    mov     r5, #1              @ remainder of 1
_toString:  
    add     r5, #48             @ convert remainder to ASCII
    ldr     r1, =binStr         @ address of binary string
    strb    r5, [r1, r9]        @ store remainder in string at pos in r9
    sub     r9, r9, #1          @ move to next position in output string
    cmp     r4, #0              @ reached zero?
    ble     _endloop
    b       _loop

_endloop:
    @ restore the link register
    ldr     lr, =lr_local
    ldr     lr, [lr]
    bx      lr

.global main

main:
    @ store the link register
    ldr     r1, =lr_temp
    str     lr, [r1]
    
    @ print a title
    ldr     r0, =title          
    bl      printf
_getValue:
    @ get the value to convert
    ldr     r0, =prompt         @ prep the arguments for scanf
    bl      printf
    ldr     r0, =pattern
    ldr     r1, =value
    bl      scanf

    @ check for valid input (0-255)
    ldr     r0, =value
    ldr     r0, [r0]
    cmp     r0, #255            @ must be <= 255
    bgt     _getValue
    cmp     r0, #0              @ must be >= 0
    blt     _getValue
    
_isValid:
    @ call the function, value is in R0
    bl      decToBin
    
    @ display output
    ldr     r0, =output         @ get address of output string
    ldr     r1, =value          @ get the original value
    ldr     r1, [r1]            @ for first parameter
    ldr     r2, =binStr         @ get the binary string
    bl      printf              @ print the result

    @ restore the link register and exit
    ldr     lr, =lr_temp
    ldr     lr, [lr]
    bx      lr

.global printf      
.global scanf
```#qcStackCode#
代码语言:javascript
复制
EN

回答 1

Code Review用户

发布于 2021-02-20 15:42:40

以下是一些可以帮助您改进程序的事情。

理解调用约定

ARM的过程调用标准表示,调用的函数可以使用寄存器r0、r1、r2和r3,而不能还原它们。此外,如果我们不修改lr,就没有必要保存并恢复它。这些都是提高程序效率的线索。

知道您的指令集

目前的代码包含以下几行:

代码语言:javascript
复制
    movs    r4, r4, lsr #1      @ divide by 2, set carry
    bcs     _odd                @ if carry set, remainder is 1
_even:
    mov     r5, #0              @ remainder of 0
    b       _toString
_odd:
    mov     r5, #1              @ remainder of 1
_toString:  
    add     r5, #48             @ convert

这是非常低效的。我们可以大大简化:

代码语言:javascript
复制
    mov     r5, #48             @ start with ASCII '0'
    movs    r4, r4, lsr #1      @ divide by 2, set carry
    adc     r5, #0              @ add zero + carry

最小分支

代码以以下奇怪的组合结束:

代码语言:javascript
复制
    ble     _endloop
    b       _loop
_endloop:

首先,注释表明我们正在寻找完全为零的值,因此我们应该只查看零标志(beqbne),而不是与ble进行签名比较。第二,为什么不简单地使用一条指令?

代码语言:javascript
复制
    beq     _loop

不过,更好的是,看下一个建议。

创建可重用代码

如果移位值变为零,则该代码当前退出。这很管用,但只有一次。换句话说,如果您使用例程解码255,然后再解码值3,您可能会感到惊讶,您的例程将错误地给出相同的二进制字符串。问题是,它依赖于binStr缓冲区来始终包含一个零字符串。更好的方法是显式地设置缓冲区的每个字节,而不是使用您在r9中已经拥有的计数器。这缩短了代码,并使其可重用。

代码语言:javascript
复制
    subs    r9, r9, #1  
    bpl     _loop

移动环不变量在循环

之外

在循环中的每一次迭代中,我们都用binStr的地址加载D23寄存器,即使r1在循环中从未改变过。更有效的方法是将r1加载到循环之外一次。

文档寄存器使用

要成为一名优秀的汇编语言程序员,关键之一是仔细管理注册使用。最基本的要求是跟踪您是如何使用寄存器的,对于您自己和将来的代码读者来说,一个很好的方法就是记录这种使用。根据上文第一项建议中的意见,我使用了以下内容:

代码语言:javascript
复制
@ Register usage:  
@   r0 - passed value
@   r1 - pointer to current string digit
@   r2 - decrementing digit offset
@   r3 - the current digit

结果

使用所有这些建议,生成的例程仅为九条指令。

代码语言:javascript
复制
decToBin:
    ldr     r1, =binStr         @ output string location
    mov     r2, #7              @ start with the LSB 
_loop:
    mov     r3, #48             @ start with ASCII zero
    movs    r0, r0, lsr #1      @ divide by 2, set carry
    adc     r3, #0              @ add the extracted bit
    strb    r3, [r1, r2]        @ store remainder in string at pos in r9
    subs    r2, r2, #1          @ decrement counter
    bpl     _loop               @ end if it has gone negative
    bx      lr

带着这些建议(双关意!)看看是否可以对代码的其余部分执行类似的清理。特别是,有一种更有效的方法来检查输入值是否在适当的范围内(提示:movs r1, r0, lsr #8)。

而且,只是为了好玩,这个答案是由一个运行在电池电源上的Raspberry Pi完全组成和测试的。

其他想法

一个更普遍有用的例程将让调用者传递一个缓冲区指针和缓冲区长度,程序将将其结果放入其中,而不是使用固定位置。看看您是否能够找到如何修改代码来实现这一点,并且不要忘记放置终止的\0字符。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/256087

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档