【问题标题】:ARM Assembly Stack Values not movingARM 程序集堆栈值不移动
【发布时间】:2020-10-11 18:33:06
【问题描述】:

我正在尝试在 ARM 汇编中构建一个基本的 4 函数计算器。我们必须使用 Push 和 Pop,这 4 个函数中的每一个都必须是自己的子程序。

由于某种原因,我无法将值传递给子例程。教授说我们不需要使用堆栈帧或帧指针,我想这很好,但我不知道为什么这不起作用。我在正确的地方使用了 PUSH 命令吗?我是否只需要推动 LR,如果是,我应该如何以及在哪里放置它?

如果格式混乱,也很抱歉。我不知道如何在 Stack Overflow 中做任何事情。

.equ READERROR, 0 @Used to check for scanf read error.
.global main @ Have to use main because of C library uses. 

main:

@*******************
begin:
@*******************

@ Welcome message.

ldr r0, =strInputPrompt  @ Put the address of my string into the first parameter
bl  printf               @ Call the C printf to display input prompt.

ldr r0, =inputPrompt1    @ Put the address of my string into the first parameter
bl  printf               @ Call the C printf to display input prompt.

ldr r0, =numInputPattern @ Setup to read in one number.
ldr r1, =intInput        @ load r1 with the address of where theinput value will be stored.

bl scanf            @ scan the keyboard
cmp r0, #READERROR       @ Check for a read error.
beq readerror            @ If there was a read error go handle it. 

ldr r3, =intInput       @ load intInput into r3
ldr r3, [r3]            @ Read the contents of intInput and store in r3
mov r2, #-1         @ load -1 into r2
cmp r3, r2          @ is the value entered less than or equal to -1?
ble invalidnums     @ if yes, print invalid message
ldr r0, =inputPrompt2    @ Put the address of my string into the first parameter
bl  printf               @ Call the C printf to display input prompt.
ldr r0, =numInputPattern @ Setup to read in one number.
ldr r1, =intInput2       @ load r1 with the address of where theinput value will be stored.

bl scanf            @ scan the keyboard
cmp r0, #READERROR       @ Check for a read error.
ldr r4, =intInput        @ load intInput into r4 
ldr r4, [r4]             @ Read the contents of intInput and store in r4
mov r3, #-1         @ load -1 into r2
cmp r4, r3          @ is the value entered less than or equal to -1?
ble invalidnums     @ if yes, print invalid message

@blt again         @ if invalid return value, ask for another attempt
@mov r1, r0
@ldr r0, =answer
@bl printf         @ print the answer
@b again           @ ask another attempt

@*******************
get_op:
@*******************

ldr r0, =inputPrompt3    @ Put the address of my string into the first parameter
bl  printf               @ Call the C printf to display input prompt.

ldr r0, =charInputPattern @ Setup to read in one number.
ldr r1, =charInput        @ load r1 with the address of where theinput value will be stored.

bl scanf            @ scan the keyboard
cmp r0, #READERROR       @ Check for a read error.
ldr r5, =charInput       @ load charInput into r5 
ldr r5, [r5]             @ Read the contents of charInput and store in r5

push {r3,r4}

cmp r5, #'+'        @ did user enter +?
beq addTest             @ if yes, go to addTest

cmp r5, #'-'        @ did user enter -?
beq subTest             @ if yes, go to subTest

cmp r5, #'*'            @ did user enter *?
beq multTest        @ if yes, go to multTest

cmp r5, #'/'            @ did user enter /?
beq divTest             @ if yes, go to divTest

b begin             @ back to beginning of program

@*******************
addTest:
@*******************
ldr r0, =addOpt        @ Put the address of my string into the first parameter
bl  printf              @ Call the C printf to display input prompt. 

bl addCalc
pop {r0}
mov r1, r0
ldr r0, =answer
bl printf

@*******************
subTest:
@*******************
ldr r0, =subOpt        @ Put the address of my string into the first parameter
bl  printf              @ Call the C printf to display input prompt. 

bl subCalc
pop {r0}
mov r1, r0
ldr r0, =answer
bl printf
@*******************
multTest:
@*******************
ldr r0, =multOpt       @ Put the address of my string into the first parameter
bl  printf              @ Call the C printf to display input prompt. 

bl multCalc
pop {r0}

@*******************
divTest:
@*******************
ldr r0, =divOpt        @ Put the address of my string into the first parameter
bl  printf              @ Call the C printf to display input prompt. 

bl divCalc
pop {r0}

@***********
addCalc:
@***********

pop {r5,r6}
add r6, r5, r6

@cmp r6, #0            @ test if upper word is non-zero (for overflow)
@vs overflow
@movgt r7, #-1     @ if overflow, load -1 as a result
@asrle r7, #1      @ if not, divide by 2
@blgt overflow     @ if overflow, print overflow
@mov r0, r7

push {r6}
bx lr          @ return

@***********
subCalc:
@***********

pop {r5,r6}
subs r6, r5, r6        @ subtract r5-r6 and store in r6
@cmp r6, #0            @ test if upper word is non-zero (for overflow)
@vs overflow
@movgt r7, #-1     @ if overflow, load -1 as a result
@asrle r7, #1      @ if not, divide by 2
@blgt overflow     @ if overflow, print overflow
@mov r0, r7

push {r6}          @ return
bx lr

@***********
multCalc:
@***********

mov r6, #0
umull r7, r6, r4, r5    @ 64-bit multiplication
@cmp r6, #0            @ test if upper word is non-zero (for overflow)
@vs overflow
@movgt r7, #-1     @ if overflow, load -1 as a result
@asrle r7, #1      @ if not, divide by 2
@blgt overflow     @ if overflow, print overflow
mov r0, r7

push {r6}          
bx lr          @ return

@***********
divCalc:
@***********


mov r6, #0
bl divLoop
@cmp r6, #0            @ test if upper word is non-zero (for overflow)
@vs overflow
@movgt r7, #-1     @ if overflow, load -1 as a result
@asrle r7, #1      @ if not, divide by 2
@blgt overflow     @ if overflow, print overflow
@mov r0, r7

push {r6}          
bx lr          @ return

@***********
divLoop:
@***********

subs r4, r4, r5        @ r4-r5 and store in r4. this will be remainder
adds r7, r7, #1        @ increment by 1. this is how many times r4 has been divided
cmp r4, r5         @ if r4 < r5 return to divCalc
bx lr

@***********
overflow:
@***********
push {lr}   
push {fp}   
mov fp, sp         @ create stack frame
ldr r0, =overstr       @ load overflow string
bl printf          @ print overflow string
pop {fp}           @ restore stack frame
pop {pc}           @ return

@***********
returnInput:
@***********
@ reset stack
add sp, #4         @ restore stack
pop {fp}           @ restore frame
pop {pc}           @ return

@***********
again:
@***********
ldr r0, =againstr
bl printf          @ print againstr
sub sp, #1         @ allocate space for one char
mov r1, sp
ldr r0, =charInputPattern
bl scanf           @ scan the keyboard
mov r0, #0
ldrsb r0, [sp], #4     @ load char and restore stack
cmp r0, #'y'       @ compare to 'y'
beq begin          @ if y, go back to the beginning
cmp r0, #'n'       @ compare to 'n'
beq myexit         @ if n, exit the program
b again            @ else, try again

@***********
invalidnums:
@***********
ldr r1, [fp, #8]
mov r0, #-1
str r0, [r1] @set the inputted value to -1
sub sp, #100       @ allocate space for a 100-character string
ldr r0, =charInputPattern
mov r1, sp
bl scanf           @ scan the keyboard
add sp, #100       @ restore stack

@*******************
myexit:
@*******************
@ End of my code. Force the exit and return control to OS

mov r7, #0x01 @SVC call to exit
svc 0         @Make the system call. 

@***********
readerror:
@***********
@ Got a read error from the scanf routine. Clear out the input buffer then
@ branch back for the user to enter a value. 
@ Since an invalid entry was made we now have to clear out the input buffer by
@ reading with this format %[^\n] which will read the buffer until the user 
@ presses the CR. 

ldr r0, =strInputPattern
ldr r1, =strInputError   @ Put address into r1 for read.
bl scanf                 @ scan the keyboard.
@  Not going to do anything with the input. This just cleans up the input buffer.  
@  The input buffer should now be clear so get another input.

b again



.data

@ Declare the strings and data needed

.balign 4
strInputPrompt: .asciz "Hello. Enter 2 non-negative integers and an operation (+, -, *, or /) and I 
will calculate the answer. \n"

.balign 4
inputPrompt1: .asciz "Enter first non-negative integer: \n "

.balign 4
inputPrompt2: .asciz "Enter second non-negative integer: \n "

.balign 4
inputPrompt3: .asciz "Enter the operation (+, -, *, or /): \n "

.balign 4
addOpt: .asciz "Addition \n "

.balign 4
subOpt: .asciz "Subtraction \n "

.balign 4
multOpt: .asciz "Multiplication \n "

.balign 4
divOpt: .asciz "Division \n "

.balign 4
answer: .asciz "The answer is: \n"

.balign 4
overstr: .asciz "Overflow \n"

.balign 4
againstr: .asciz "Try another calculation? \n"

.balign 4
nl: .asciz "\n"

.balign 4
invalidStr: .asciz "Invalid input, try again \n"

@ Format pattern for scanf call.

.balign 4
numInputPattern: .asciz "%d"  @ integer format for read. 

.balign 4
charInputPattern: .asciz "%s"  @ character format for read.

.balign 4
 intInput: .word 0   @ Location used to store the user input. 

.balign 4
intInput2: .word 0   @ Location used to store the user input. 

.balign 4
charInput: .word 0   @ Location used to store the user input. 

.balign 4
strInputPattern: .asciz "%[^\n]" @ Used to clear the input buffer for invalid input. 

.balign 4
strInputError: .skip 100*4  @ User to clear the input buffer for invalid input. 


@ Let the assembler know these are the C library functions. 

.global printf

.global scanf

@end of code and end of file. Leave a blank line after this.
`

【问题讨论】:

    标签: assembly arm stack


    【解决方案1】:

    尝试使用STMDB 指令而不是PUSHSTMDB 代表

    在每次传输之前存储多个寄存器递减地址。 (取自RE,适合初学者)

    Here is markdown guide

    【讨论】:

    • pushstmdb 的别名,使用堆栈指针作为目标。
    【解决方案2】:

    我是否在正确的地方使用了 PUSH 命令?

    不,你没有。
    push 将寄存器放入堆栈并递减堆栈指针。 pop 将堆栈中的数据读入寄存器并增加堆栈指针。 push-ed 数据的数量应与pop-ed 数据的数量匹配。


    我是否只需要推动 LR,如果是,我应该如何以及在哪里放置它?

    取决于,如果您的子例程调用另一个子例程,您需要保留 LR。因为调用第二个子例程(如bl func2)会改变LR,你需要原来的LR 从第一个子例程返回。一般做法是将LR 保留在堆栈中,但您也可以使用其他非易失性寄存器。

    一般来说,您的整个代码都是一团糟,抱歉。
    仅举几个例子给你一个想法。

    mov r3, #-1         @ load -1 into r2
    cmp r4, r3          @ is the value entered less than or equal to -1?
    ...
    bl  printf               @ Call the C printf to display input prompt.
    ...
    bl scanf            @ scan the keyboard
    ...
    
    push {r3,r4}   <--- HERE
    
    cmp r5, #'+'        @ did user enter +?
    
    • 你想在那里推动什么?在调用printfscanf 之后,r3 将包含垃圾。或者 -1 如果任何一个函数会使用这个寄存器(不太可能)

    @*******************
    addTest:
    @*******************
    ldr r0, =addOpt        @ Put the address of my string into the first parameter
    bl  printf              @ Call the C printf to display input prompt. 
    
    bl addCalc
    pop {r0}
    mov r1, r0
    ldr r0, =answer
    bl printf
    
    @*******************
    
    @***********
    addCalc:
    @***********
    
    pop {r5,r6}       <--- (1)
    add r6, r5, r6
    ...    
    push {r6}         <--- (2)
    bx lr          @ return
    
    • 您在 (1) 中的 regs r5r6 中添加了什么?看起来像以前推送的r3,r4...一般来说这是一种非常糟糕的传递参数的方法。同样r5 会在这里包含垃圾。
    • (2) 从子例程返回值是一种非常糟糕的方式。

    有“Arm体系结构的过程调用标准”,善待自己和他人,按照标准在函数内部传递参数并返回函数结果。


    cmp r5, #'+'        @ did user enter +?
    beq addTest             @ if yes, go to addTest
    
    cmp r5, #'-'        @ did user enter -?
    beq subTest             @ if yes, go to subTest
    
    cmp r5, #'*'            @ did user enter *?
    beq multTest        @ if yes, go to multTest
    
    cmp r5, #'/'            @ did user enter /?
    beq divTest             @ if yes, go to divTest
    
    b begin             @ back to beginning of program
    
    @*******************
    addTest:
    @*******************
    ldr r0, =addOpt        @ Put the address of my string into the first parameter
    bl  printf              @ Call the C printf to display input prompt. 
    
    bl addCalc
    pop {r0}
    mov r1, r0
    ldr r0, =answer
    bl printf
                          <---- HERE ----
    @*******************
    subTest:
    @*******************
    ldr r0, =subOpt        @ Put the address of my string into the first parameter
    bl  printf              @ Call the C printf to display input prompt. 
    
    bl subCalc
    pop {r0}
    mov r1, r0
    ldr r0, =answer
    bl printf
    
    • 你最大的问题在这里。假设用户选择了+,接下来你要做的就是跳转到addTest (beq addTest)。之后bl addCalc 将从堆栈中删除r3r4 (pop {r5,r6})。到目前为止一切顺利。
      但在那之后你打印答案(ldr r0, =answer/bl printf)并继续直接到subTest

      subTest 中执行 pop {r5,r6} 后,一百万个价值的问题是什么? 答:没有人知道,因为addCalc 已经删除了之前推送的数据(例如单个push,多个pop-s)。

    所以利用调用子程序转换,你的问题就烟消云散了。

    【讨论】:

      猜你喜欢
      • 2014-11-28
      • 2013-08-12
      • 2017-10-11
      • 2011-01-26
      • 1970-01-01
      • 1970-01-01
      • 2018-08-29
      • 2011-11-26
      • 2013-07-05
      相关资源
      最近更新 更多