汇编语言MIPS的入门

#最近一段时间都在忙着带汇编语言的课程。顺道自己也学了一下。

##Monash University用Mips来介绍汇编语言环境。配合MARS Simulator进行测试。

##其实隐隐觉得MARS里面有一些BUG, 很多时候程序的过程好像都不是很稳定。

  1. Convention
    不同的写法和风格对应不同的convention。具体的中文翻译我说不上来,但是指的应该就是你写代码时参照的风格模板。 汇编与高级语言不同,其写法和逻辑自由度很高。 很多时候在不考虑效率的前提下可以衍生出很多方式去完成同一问题。这样会带来很多问题,尤其是别的程序员调用你的代码的时候,对你代码部分内存的调用,返回值的位置等等都不能准确的预判。这就使你编码的模板变得很重要。目测现存的汇编存在众多模板/convention可以使用。但要确认你在同一项目中使用相同的convention。
    #这是一份我觉得比较好用的Convention的reference sheet

    要注意的是这并不是全部的指令,对于很多版本的convention还有一些高级指令可以调用。这个版本只是更方便于学生理解。

  2. Loops
    在很多高级语言中for 和 while loop都只是一句话的事情。 而在mips中,写代码的人却需要考虑loop的位置,branch的具体跳转位置已经else部分应该放在哪里。这种特性就让while loop更容易在mips中实现,而for loop则变得稍微有些蹩脚。

##对于python 的while loop

n = int(input("Enter integer: "))

while (n > 1):
print(n)
if n % 2 == 0:
n = n//2
else:
n = 3*n + 1
print(n)

转换到mips的逻辑大致是:

aLaberCalledLoop
.
beq/bne…aLaberCalledElse
.
.
j aLaberCalledLoop

aLaberCalledElse

这里用到3个概念:laber, jump 和 branch。 都不难理解。如有问题请自行翻阅info sheet。
另外对于jump,具体来说可以被分为4种:
JumpsInMips
上面的例子用的是最简单的一直,其余的几种都与function的调用与返回有关。其实琢磨一下也没有什么,只是对于$pc(program counter)的值进行写入从而操纵下一行代码读取的位置罢了。

##对于for loop
基本的概念是和while loop差不多。这里唯一需要注意的就是else里面statement的写法和在程序中出现的位置。

python 代码

def even_product(a_list):
product = 1
for x in a_list:
if x%2 ==0:
product=product*x
return product

当转换到mips时:

.text

even_product: # save $fp and $ra
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $fp, 0($sp)

# update $fp
addi $fp, $sp, 0

# allocate local variables
addi $sp, $sp, -8

# setup product
li $t0, 1
sw $t0, -4($fp) # save product

# setup i
li $t0, 0
sw $t0, -8($fp) # save i

prodloop: lw $t0, -8($fp) # $t0 = i
lw $t1, 8($fp) # $t1 = list
lw $t2, 0($t1) # $t2 = len(list)
bge $t0, $t2, endprod # check if i < len(list)

# restore x
lw $t0, -8($fp) # $t0 = i

mul $t3, $t0, 4 # $t3 = 4*i
addi $t3, $t3, 4 # $t3 = 4*i + 4
add $t3, $t3, $t1 # $t3 = address of list[i]
lw $t3, 0($t3) # $t3 = list[i] = x

# if x % 2 == 0
li $t4, 2
div $t3, $t4
mfhi $t4
bnez $t4, else

# product = product*x
lw $t4, -4($fp)
mul $t4, $t4, $t3
sw $t4, -4($fp)

else: lw $t0, -8($fp)
addi $t0, $t0, 1
sw $t0, -8($fp)

j prodloop


endprod: # set return
lw $v0, -4($fp)

# deallocate variables
addi $sp, $sp, 8

# restore $fp and $ra
lw $fp, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
jr $ra

需要注意的是,很多时候即使for loop中的statement成立,你的代码很可能还是需要执行接下的来的部分,也就是包含else的部分。这种情况下就需要谨慎对待else中的内容。基本上就意味着else中的内容在汇编中并不完全等于在高级语言中的内容。

  1. stack

首先,在mips中,内存被从上到下分成5块:
——–top———
.data
——-memory——-
.text
——-memory——-
heap
——-memory——-
free
spaces
——-memory——-
stack
——–bottom——
这样不难看出,data+text+heap位于内存的顶部,stack位于底部。而heap和stack中间的区域是未被利用的内存空间。使用这种排列方法的原因很简单:应为在程序初始化的时候data+text的大小是已知的。让heap从上往下生长,而stack从底部反向向上生长,所有的空白内存都留在了两者之间。这既保证了最大效率的利用内存,又不用担心如何分配stack的起始点。
而这种分配带来的问题就是stack中内存使用的计算是反向的。每次给一个新的function初始化内存的时候,你都需要把pointer向上反向移动。比如现在pointer在0x2ff04(16进制内存地址,不熟悉的小伙伴小时候肯定没用过金手指), 你要内一个新的程序初始化一个字段的内存,你需要减去4bits (0x2ff04-4 = 0x2ff00)。0x2ff00则是你新内存指针的位置。

  1. list
    mips其实并不提供list这种高级数据格式。对于列表的操作更大程度上依赖于程序员对于stack/heap的理解。

——–top———
0x7FFF3128
——-memory——-
0x7FFF312C
——-memory——-
0x7FFF3130
——-memory——-
0x7FFF3134
——-memory——-
0x7FFF3138
——-bottom——

比如在如上的内存空间中,如果一开始,你的stack pointer($sp)在0x7FFF3138的位置,你想写入一个3个单元的list。你需要做的如下:
(1)把$sp减去3,向上反向移动3格。新的$sp指向0x7FFF312C
(2)把list中的第一个值写入内存空间0x7FFF312C
(3)把list中的第二个值写入内存空间0x7FFF3130
(4)把list中的第三个值写入内存空间0x7FFF3134
这样你就得到了一个list,占用三个内存空间。需要注意的是mips并不知道这是一个list。作为程序员,在之后的读取中你也要做类似的操作,在记住列表第一个元素的内存地址后,每+4便得到列表下一个值的位置。

先写这么多吧,在学习路上,如有错路请见谅并在回复中帮我指出谢谢!

加载评论框需要翻墙