我正在“玩”与手臂组装在一个树莓派,阅读各种教程和帖子帮助-不,这不是一个大学作业,我太老了,大学!我很高兴让下面的代码获得一个十进制值(介于0到255之间),并使用scanf()和printf()输出相当于屏幕的二进制字符串,但我很想知道那些经验更丰富的人是否会认为这是一个“天真”的解决方案?有什么“陷阱”吗?有什么更好的方法来解决这个问题吗?
我认为应该有一种更好的方法来声明字符串来保存输出,以及字符串是如何从LSB构建的(使用R9)。
/* 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#发布于 2021-02-20 15:42:40
以下是一些可以帮助您改进程序的事情。
ARM的过程调用标准表示,调用的函数可以使用寄存器r0、r1、r2和r3,而不能还原它们。此外,如果我们不修改lr,就没有必要保存并恢复它。这些都是提高程序效率的线索。
目前的代码包含以下几行:
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这是非常低效的。我们可以大大简化:
mov r5, #48 @ start with ASCII '0'
movs r4, r4, lsr #1 @ divide by 2, set carry
adc r5, #0 @ add zero + carry代码以以下奇怪的组合结束:
ble _endloop
b _loop
_endloop:首先,注释表明我们正在寻找完全为零的值,因此我们应该只查看零标志(beq或bne),而不是与ble进行签名比较。第二,为什么不简单地使用一条指令?
beq _loop不过,更好的是,看下一个建议。
如果移位值变为零,则该代码当前退出。这很管用,但只有一次。换句话说,如果您使用例程解码255,然后再解码值3,您可能会感到惊讶,您的例程将错误地给出相同的二进制字符串。问题是,它依赖于binStr缓冲区来始终包含一个零字符串。更好的方法是显式地设置缓冲区的每个字节,而不是使用您在r9中已经拥有的计数器。这缩短了代码,并使其可重用。
subs r9, r9, #1
bpl _loop之外
在循环中的每一次迭代中,我们都用binStr的地址加载D23寄存器,即使r1在循环中从未改变过。更有效的方法是将r1加载到循环之外一次。
要成为一名优秀的汇编语言程序员,关键之一是仔细管理注册使用。最基本的要求是跟踪您是如何使用寄存器的,对于您自己和将来的代码读者来说,一个很好的方法就是记录这种使用。根据上文第一项建议中的意见,我使用了以下内容:
@ Register usage:
@ r0 - passed value
@ r1 - pointer to current string digit
@ r2 - decrementing digit offset
@ r3 - the current digit使用所有这些建议,生成的例程仅为九条指令。
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字符。
https://codereview.stackexchange.com/questions/256087
复制相似问题