将C函数转换为MIPS程序集

时间:2016-04-03 20:39:05

标签: c assembly mips mars-simulator

我目前正在学习MIPS汇编,我正在尝试将以下C函数转换为MIPS汇编:

int count (int a[], int n, int x)
{
int res = 0;
int i = 0;
int j = 0;
int loc[];
for(i = 0; i != n; i++)
if(a[i] == x)
{
    res = res + 1;
    loc [j] = i;
    j = j + 1;
}
return res, loc;
}

我已经成功转换了大部分内容,我相信我已成功返回res(值为1),虽然我不确定返回loc(我也得到值为1,并且我不认为这是对的)。但是,我在使用这个程序时遇到了困难,我不确定如何确保loc返回正确的值或如何编写代码来执行此操作。

这是我的汇编代码:

.data
a: .word 5,6,7,8,9,10
n: .word
x: .word
res: .word 0
i: .word 0
jj: .word 0
loc: .space 40

.text
main:
la $s0, a
lw $s1, res
lw $s2, x
lw $t0, i
lw $t1, jj
lw $t2, n
la $s3, loc
li $t4, 6

start:
sll $t3, $t0, 2
add $t5, $t3, $s0
lw $t4, 0($t5)

beq $t0, $t4, start
addi $t0, $t0, 1
beq $t0, $t2, exit

addi $s1, $s1, 1
sll $t7, $t1, 2
add $t6, $s3, $t7
sw $t0, 0($t6)
addi $t1, $t1, 1

exit:
li $v0, 1
add $a0, $s1, $zero
syscall

li $v0, 1
add $a1, $s3, $zero
syscall

非常感谢任何帮助,指示或建议。

编辑:我修改了我的代码,现在收到res返回0和" 268501028"为loc。不知道这个号码的来源。

.data
a: .word 5,6,7,8,9,10
n: .word #n
x: .word #x
res: .word 0
i: .word 0
jj: .word 0
loc: .space 40

.text
main:
la $s0, a
lw $s1, res
lw $s2, x
lw $t0, i
lw $t1, jj
lw $t2, n
la $s3, loc
li $t4, 6

start:
beq $t0, $t2, exit #for(i = 0; i != n; i++)
bne $s0, $s2, else #if(a[i] == x)
j start

else:
addi $s1, $s1, 1 #res = res + 1;
sw $t0, ($t1) #loc [j] = i;
addi $t1, $t1, 1 #j = j+1
addi $t0, $t0, 1 #Increment i

addi $s3, $s3, 4 #Setting next element for loc
addi $s0, $s0, 4 #Setting next element for a
j start

exit:
li $v0, 1
move $a0, $s1
syscall

li $v0, 1
move $a0, $s3
syscall

1 个答案:

答案 0 :(得分:4)

好的,有一些错误。我已经注释了源代码并添加了#B; BUG:"高亮一点。然后我创建了一个清理并更正的版本

这是您的原始代码 - 没有错误修复,只是注释[请原谅无偿风格的清理]:

# int
# count(int a[], int n, int x)
# {
#   int res = 0;
#   int i = 0;
#   int j = 0;
#   int loc[n];
#
#   for (i = 0; i != n; i++) {
#       if (a[i] == x) {
#           res = res + 1;
#           loc[j] = i;
#           j = j + 1;
#       }
#   }
#
#   return res, loc;
# }

    .data
a:          .word       5,6,7,8,9,10
n:          .word
x:          .word
res:        .word       0
i:          .word       0
jj:         .word       0
loc:        .space      40
nl:         .asciiz     "\n"

    .text

    .globl  main

main:
    la      $s0,a
    lw      $s1,res
    lw      $s2,x
    lw      $t0,i
    lw      $t1,jj
    lw      $t2,n
    la      $s3,loc
    li      $t4,6                   # BUG: extraneous (gets trashed below)

start:
    sll     $t3,$t0,2               # get i << 2
    add     $t5,$t3,$s0             # get &a[i]
    lw      $t4,0($t5)              # fetch it

    # BUG: we're comparing a[i] against i but we want to compare against x
    # _and_ we want to flip the sense of the branch
    beq     $t0,$t4,start           # is it a match? if yes, loop

    addi    $t0,$t0,1               # increment i
    beq     $t0,$t2,exit            # i == n? if no, loop. if yes, exit

    # BUG: the indexing here is wrong
    addi    $s1,$s1,1               # j += 1
    sll     $t7,$t1,2               # get jj << j
    add     $t6,$s3,$t7             # &loc[jj << j] (BUG: we want &loc[j])
    sw      $t0,0($t6)              # set it to i
    addi    $t1,$t1,1               # jj += 1

    # BUG: we should loop here and _not_ fall through

exit:
    # print j (with newline)
    li      $v0,1
    add     $a0,$s1,$zero
    syscall
    li      $v0,4
    la      $a0,nl
    syscall

    # print _address_ of loc[0]
    # BUG: if we care to print anything, we should print the _values_ of the
    # whole array
    li      $v0,1
    # BUG: this should be a0 and _not_ a1
    ###add      $a1,$s3,$zero
    add     $a0,$s3,$zero
    syscall
    li      $v0,4
    la      $a0,nl
    syscall

    li      $v0,10                  # exit program
    syscall

这里是已清理和更正的版本。我不得不做一些重组和简化以使其工作,所以它可能看起来有点&#34;外星人&#34;首先。但是,我尽可能保留您的注册用法。

我还增加了a数组的大小,并为x值添加了用户提示:

# int
# count(int a[], int n, int x)
# {
#   int i = 0;
#   int j = 0;
#   int loc[n];
#
#   for (i = 0; i != n; i++) {
#       if (a[i] == x) {
#           loc[j] = i;
#           j += 1;
#       }
#   }
#
#   return j, loc;
# }

    .data
a:          .word       5,6,7,8,9,10
    .word   5,6,7,8,9,10
    .word   5,6,7,8,9,10
    .word   5,6,7,8,9,10
    .word   5,6,7,8,9,10

ae:

loc:        .space      1000

prompt:     .asciiz     "Enter x value: "
msgnl:      .asciiz     "\n"
msgj:       .asciiz     "j: "
msgloc:     .asciiz     "loc: "

    .text

# main -- main program
#
# RETURNS [sort of as this is a main program]:
#   s1 -- j value (count of elements in "loc")
#   loc -- filled in indexes into "a" array of matches to x
#
# registers:
#   s0 -- a (base address of "a" array)
#   t2 -- n (number of elements in "a" array)
#
#   s2 -- x (value to match)
#   t0 -- i (current index into "a" array)
#   s3 -- loc (base address of "loc" array)
#   s1 -- j (current index into "loc" array)
#
#   t6 -- quick temporary [reusable]
#   t7 -- used in array offset/index calculations [reusable]
    .globl  main
main:
    # prompt for x value
    li      $v0,4                   # syscall: print string
    la      $a0,prompt
    syscall

    # read in x value
    li      $v0,5                   # syscall: read integer
    syscall
    move    $s2,$v0

    # get address of "a" array and compute length
    la      $s0,a                   # get &a[0]
    la      $t2,ae                  # get address of &a[n]
    sub     $t2,$t2,$s0             # get number of bytes in a
    srl     $t2,$t2,2               # get number of words in a (i.e. n)

    li      $t0,0                   # i = 0
    li      $s1,0                   # j = 0
    la      $s3,loc                 # base address of loc array

# main matching loop
loop:
    sll     $t7,$t0,2               # get i << 2
    add     $t7,$t7,$s0             # get &a[i]
    lw      $t6,0($t7)              # fetch from it
    bne     $t6,$s2,next            # a[i] == x? if no, advance to next element

    # add new "i" value to loc array
    sll     $t7,$s1,2               # get j << 2
    add     $t7,$s3,$t7             # &loc[j << 2]
    sw      $t0,0($t7)              # store i into loc
    addi    $s1,$s1,1               # j += 1

next:
    addi    $t0,$t0,1               # i += 1
    blt     $t0,$t2,loop            # i < n? if yes, loop (or, we're done)

# done with calculation/fill loop
done:
    la      $s6,msgj                # get prefix string
    move    $s7,$s1                 # get j
    jal     prtnum                  # pretty print the number

    blez    $s1,exit                # bug out if _no_ values in loc

    # prepare to print all values of loc
    la      $t6,loc                 # base address of "loc"
    li      $t7,0                   # initial index

# loop and print all values of loc
prtlocloop:
    la      $s6,msgloc              # prefix string
    lw      $s7,($t6)               # get loc[...]
    jal     prtnum                  # pretty print the number

    add     $t6,$t6,4               # increment address
    add     $t7,$t7,1               # increment index
    blt     $t7,$s1,prtlocloop      # done? if no, loop

exit:
    li      $v0,10                  # exit program
    syscall

# prtnum -- print a number with a prefix string on a single line
#
# arguments:
#   s6 -- prefix string
#   s7 -- value to print
#
# registers:
#   v0 -- syscall number [trashed]
#   a0 -- syscall argument [trashed]
prtnum:
    li      $v0,4                   # syscall: print string
    move    $a0,$s6                 # string to print
    syscall

    li      $v0,1                   # syscall: print integer
    move    $a0,$s7                 # value to print
    syscall

    li      $v0,4                   # syscall: print string
    la      $a0,msgnl
    syscall

    jr      $ra                     # return

<强>更新

  

printprtnum之间究竟有什么区别?

print是循环顶部的标签,用于打印loc中的值。 prtnum是子例程/函数,用于打印单个号码。

我添加了prtnum来演示函数的使用,并避免不必要地复制某些代码。

  

他们不能正确合并吗?

当然,有一些警告。我做了一些轻微的/化妆品编辑,试图让事情变得更清晰。特别是,我将print:重命名为prtlocloop:,以使其角色更加清晰。

syscall(1) for&#34; print integer&#34;只打印整数,但添加任何空格或换行符来分隔它们(即它正好,如printf("%d",a0))。所以,我们需要某些东西

最初,我刚才有syscall(print_integer)。有了这个,我们得到一个&#34;非常长的&#34; 号码。然后,我添加了syscall(4)来打印换行符。这很好,除了输出有点混淆,哪个值是j,哪个是loc值。

(1)所以,我添加了&#34;前缀&#34;串。因此,每个号码都会变成三个系统调用。

(2)这在两个位置使用:打印j 以打印loc值。

两个或多个地方的相同代码。这是&#34;将代码拆分为功能&#34;的标准标准。在任何语言中。这是一个设计/风格选择[所以没有绝对的答案]。

因此,在(1)和(2)中,我将其移至prtnum函数。实际上,我首先编写了prtnum函数 ,因为我已经知道了结构,并在输出后添加了前缀参数&#34;看起来很丑陋&#34; [对我]。

当我第一次对其进行编码时,我使用"j: "作为j,并为" "使用loc前缀。它看起来仍然有点时髦。因此,我将前缀更改为"loc: "以保持一致。

可以内联吗?当然。但是,除了打印数字本身,我们仍然需要添加分离器。因此,我们需要每个数字两个系统调用来执行此操作。

如果我们想将所有数字放在同一输出线上,则分离器可以是空格。很好的短矢量。这需要稍微改变现在存在的代码,我们必须添加换行的最终输出以关闭该行。对于较长的阵列[可能不适合单行],每行一个[可能]更整洁。

我们只需要打印jloc。如果问题表明我们必须打印a,然后j,然后打印loc,我就会采取相反的方式。

我会将prtlocloop更改为另一个函数(例如prtarray),它将在给定数组上循环并为每个元素调用prtnum

第一步是让计算循环正确。第二个是印刷。但是,有时,它们必须一起完成。 (即)你如何调试你无法看到的东西?

因此,如果计算正确,您可以以您选择的任何方式自由重新编码输出打印。 prtnum只是我的方式。但是,它是没有意味着唯一的方式。

除了使用asm指令的基本机制之外,选择与任何其他语言一样[特别是C]。好的评论,选择最简单,最有效的方法来构建/拆分代码,使用描述性变量名称等。评论应该显示&#34; intent&#34;,&#34; what / why&#34;。 asm指令是&#34; how&#34;。

旁注:有些OP很难理解{<1}} [你已经了解]的工作原理。他们只是没有&#34;得到&#34;左移2乘以4乘以并将索引值转换为字节/地址偏移的事实。所以,你可能已经领先于游戏......

昨天,我给出了一个mips问题的答案,我走了另一条路,建议内联两个函数。问题是使用形式为sll的泰勒级数展开[项的总和]来计算sin(x)

使用内联,可以重复使用系列中上一个术语的部分结果,而不必从头开始重新计算每个术语。对于多种功能而言,[方便]这是不可能的。

我没有编写mips代码,但我编写了C /伪代码:mips program to calculate sin(x)生成的mips代码[可能]会更简单,并且肯定会运行得更快。